import toxcore import std/asyncdispatch, std/base64, std/json, std/httpclient, std/os, std/strutils {.passL: "-lcrypto".} const readmeText = readFile "README.md" spaceApiUrl = "http://spaceapi.hq.c3d2.de:3000/spaceapi.json" saveFileName = "hqtoxbot.save" adminIds = [ toAddress "DF0AC9107E0A30E7201C6832B017AC836FBD1EDAC390EE99B68625D73C3FD929FB47F1872CA4" # Emery ] proc bootstrap(bot: Tox) = const servers = [ ( "tox.neuland.technology", "15E9C309CFCB79FDDF0EBA057DABB49FE15F3803B1BFF06536AE2E5BA5E4690E".toPublicKey ) ] for host, key in servers.items: bot.bootstrap(host, key) proc addAdmin(bot: Tox; id: Address) = try: discard bot.addFriend( id, "You have been granted administrative rights to " & bot.name) except ToxError: discard proc updateStatus(bot: Tox; http: AsyncHttpClient) {.async.} = try: let rsp = await http.get(spaceApiUrl) body = await rsp.body space = parseJson body status = $(space["status"]) if bot.statusMessage != status: bot.statusMessage = unescape $(space["status"]) except: bot.statusMessage = "status update failed" type Command = enum doorpass, help, invite, lock, readme, revoke, unlock, proc setup(bot: Tox) = let http = newAsyncHttpClient() addTimer(20*1000, oneshot = false) do (fd: AsyncFD) -> bool: asyncCheck updateStatus(bot, http) let schalterClient = newAsyncHttpClient() schalterClient.headers = newHttpHeaders() let conference = bot.newConference() `title=`(bot, conference, "C3D2") bot.onFriendConnectionStatus do (f: Friend; status: Connection): if status != TOX_CONNECTION_NONE: bot.invite(f, conference) bot.onFriendReadReceipt do (f: friend; id; MessageId): discard """TODO some commands should be defered until a read receipt is acquired. Maybe just revoke.""" bot.onFriendMessage do (f: Friend; msg: string; kind: MessageType): proc reply(msg: string) = discard bot.send(f, msg) try: var words = msg.split(' ') if words.len < 1: words.add "help" if words.len < 2: words.add "" case parseEnum[Command](words[0].normalize) of help: var cmd = help if words[1] != "": try: cmd = parseEnum[Command](words[1].normalize) except: reply("$1 is not a help topic" % words[1]) case cmd: of doorpass: reply("""Set the authorization string for opening the door lock. """ & """The authorization string will peristent until the bot is restarted.""") of help: var resp = """Return help message for the following commands:""" for e in Command: resp.add "\n\t" resp.add $e reply(resp) of invite: reply """Invite a new user to the bot.""" of lock: reply("""Lock the HQ door. """ & """If the door is already open his command is not effective.""") of readme: reply """Return bot README""" of revoke: reply """Remove yourself from the bot roster.""" of unlock: reply """Unlock the HQ door.""" of doorpass: schalterClient.headers["Authorization"] = "Basic " & base64.encode(words[1], newline = "") of invite: for id in words[1..words.high]: try: let address = id.toAddress other = bot.addFriend(address, "You have been invited to the $1 by $2 ($3)" % [ bot.name, bot.name(f), $bot.publicKey(f) ]) bot.invite(other, conference) except: reply(getCurrentExceptionMsg()) of lock: bot.typing(f, true) let fut = schalterClient.post("http://schalter.hq.c3d2.de/door/lock") fut.addCallback do (): bot.typing(f, false) if fut.failed: reply("lock failed") else: reply(fut.read.status) of readme: reply readmeText of revoke: reply """Tchuss""" discard bot.delete(f) of unlock: bot.typing(f, true) let fut = schalterClient.post("http://schalter.hq.c3d2.de/door/unlock") fut.addCallback do (): bot.typing(f, false) if fut.failed: reply("unlock failed") else: reply(fut.read.status) except: reply(getCurrentExceptionMsg()) proc newBot(name: string): Tox = result = newTox do (opts: Options): opts.localDiscoveryEnabled = true opts.ipv6Enabled = true if existsFile saveFileName: opts.saveDataType = TOX_SAVEDATA_TYPE_TOX_SAVE opts.savedata = readFile(saveFileName) result.name = name result.setup() for id in adminIds: result.addAdmin(id) result.bootstrap() echo result.name, " is at ", result.address proc main() = let bot = newBot("HQ Bot") writeFile(saveFileName, bot.saveData) echo "DHT port and key: ", bot.udpPort, " ", bot.dhtId while true: iterate bot poll bot.iterationInterval main()