You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

hqtoxbot.nim 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import toxcore
  2. import std/asyncdispatch, std/base64, std/json, std/httpclient, std/os, std/strutils
  3. {.passL: "-lcrypto".}
  4. const
  5. readmeText = readFile "README.md"
  6. spaceApiUrl = "http://spaceapi.hq.c3d2.de:3000/spaceapi.json"
  7. saveFileName = "hqtoxbot.save"
  8. adminIds = [
  9. toAddress "DF0AC9107E0A30E7201C6832B017AC836FBD1EDAC390EE99B68625D73C3FD929FB47F1872CA4"
  10. # Emery
  11. ]
  12. proc bootstrap(bot: Tox) =
  13. const servers = [
  14. ( "tox.neuland.technology",
  15. "15E9C309CFCB79FDDF0EBA057DABB49FE15F3803B1BFF06536AE2E5BA5E4690E".toPublicKey
  16. )
  17. ]
  18. for host, key in servers.items:
  19. bot.bootstrap(host, key)
  20. proc addAdmin(bot: Tox; id: Address) =
  21. try:
  22. discard bot.addFriend(
  23. id, "You have been granted administrative rights to " & bot.name)
  24. except ToxError:
  25. discard
  26. proc updateStatus(bot: Tox; http: AsyncHttpClient) {.async.} =
  27. try:
  28. let
  29. rsp = await http.get(spaceApiUrl)
  30. body = await rsp.body
  31. space = parseJson body
  32. status = $(space["status"])
  33. if bot.statusMessage != status:
  34. bot.statusMessage = unescape $(space["status"])
  35. except:
  36. bot.statusMessage = "status update failed"
  37. type Command = enum
  38. doorpass,
  39. help,
  40. invite,
  41. lock,
  42. readme,
  43. revoke,
  44. unlock,
  45. proc setup(bot: Tox) =
  46. let http = newAsyncHttpClient()
  47. addTimer(20*1000, oneshot = false) do (fd: AsyncFD) -> bool:
  48. asyncCheck updateStatus(bot, http)
  49. let schalterClient = newAsyncHttpClient()
  50. schalterClient.headers = newHttpHeaders()
  51. let conference = bot.newConference()
  52. `title=`(bot, conference, "C3D2")
  53. bot.onFriendConnectionStatus do (f: Friend; status: Connection):
  54. if status != TOX_CONNECTION_NONE:
  55. bot.invite(f, conference)
  56. bot.onFriendReadReceipt do (f: friend; id; MessageId):
  57. discard """TODO some commands should be defered until a read receipt is acquired. Maybe just revoke."""
  58. bot.onFriendMessage do (f: Friend; msg: string; kind: MessageType):
  59. proc reply(msg: string) =
  60. discard bot.send(f, msg)
  61. try:
  62. var words = msg.split(' ')
  63. if words.len < 1:
  64. words.add "help"
  65. if words.len < 2:
  66. words.add ""
  67. case parseEnum[Command](words[0].normalize)
  68. of help:
  69. var cmd = help
  70. if words[1] != "":
  71. try: cmd = parseEnum[Command](words[1].normalize)
  72. except:
  73. reply("$1 is not a help topic" % words[1])
  74. case cmd:
  75. of doorpass:
  76. reply("""Set the authorization string for opening the door lock. """ &
  77. """The authorization string will peristent until the bot is restarted.""")
  78. of help:
  79. var resp = """Return help message for the following commands:"""
  80. for e in Command:
  81. resp.add "\n\t"
  82. resp.add $e
  83. reply(resp)
  84. of invite:
  85. reply """Invite a new user to the bot."""
  86. of lock:
  87. reply("""Lock the HQ door. """ &
  88. """If the door is already open his command is not effective.""")
  89. of readme:
  90. reply """Return bot README"""
  91. of revoke:
  92. reply """Remove yourself from the bot roster."""
  93. of unlock:
  94. reply """Unlock the HQ door."""
  95. of doorpass:
  96. schalterClient.headers["Authorization"] =
  97. "Basic " & base64.encode(words[1], newline = "")
  98. of invite:
  99. for id in words[1..words.high]:
  100. try:
  101. let
  102. address = id.toAddress
  103. other = bot.addFriend(address, "You have been invited to the $1 by $2 ($3)" % [ bot.name, bot.name(f), $bot.publicKey(f) ])
  104. bot.invite(other, conference)
  105. except:
  106. reply(getCurrentExceptionMsg())
  107. of lock:
  108. bot.typing(f, true)
  109. let fut = schalterClient.post("http://schalter.hq.c3d2.de/door/lock")
  110. fut.addCallback do ():
  111. bot.typing(f, false)
  112. if fut.failed:
  113. reply("lock failed")
  114. else:
  115. reply(fut.read.status)
  116. of readme:
  117. reply readmeText
  118. of revoke:
  119. reply """Tchuss"""
  120. discard bot.delete(f)
  121. of unlock:
  122. bot.typing(f, true)
  123. let fut = schalterClient.post("http://schalter.hq.c3d2.de/door/unlock")
  124. fut.addCallback do ():
  125. bot.typing(f, false)
  126. if fut.failed:
  127. reply("unlock failed")
  128. else:
  129. reply(fut.read.status)
  130. except:
  131. reply(getCurrentExceptionMsg())
  132. proc newBot(name: string): Tox =
  133. result = newTox do (opts: Options):
  134. opts.localDiscoveryEnabled = true
  135. opts.ipv6Enabled = true
  136. if existsFile saveFileName:
  137. opts.saveDataType = TOX_SAVEDATA_TYPE_TOX_SAVE
  138. opts.savedata = readFile(saveFileName)
  139. result.name = name
  140. result.setup()
  141. for id in adminIds: result.addAdmin(id)
  142. result.bootstrap()
  143. echo result.name, " is at ", result.address
  144. proc main() =
  145. let bot = newBot("HQ Bot")
  146. writeFile(saveFileName, bot.saveData)
  147. echo "DHT port and key: ", bot.udpPort, " ", bot.dhtId
  148. while true:
  149. iterate bot
  150. poll bot.iterationInterval
  151. main()