hqtoxbot/src/hqtoxbot.nim

177 lines
5.0 KiB
Nim
Raw Normal View History

2019-12-17 04:53:51 +01:00
import toxcore
2019-12-25 15:40:09 +01:00
import std/asyncdispatch, std/base64, std/json, std/httpclient, std/os, std/strutils
2019-12-17 04:53:51 +01:00
2020-01-23 15:14:41 +01:00
{.passL: "-lcrypto".}
2019-12-25 15:40:09 +01:00
const
2019-12-26 06:40:15 +01:00
readmeText = readFile "README.md"
2019-12-25 15:40:09 +01:00
spaceApiUrl = "http://spaceapi.hq.c3d2.de:3000/spaceapi.json"
saveFileName = "hqtoxbot.save"
adminIds = [
toAddress "DF0AC9107E0A30E7201C6832B017AC836FBD1EDAC390EE99B68625D73C3FD929FB47F1872CA4"
2019-12-17 04:53:51 +01:00
# Emery
]
proc bootstrap(bot: Tox) =
2019-12-26 06:40:15 +01:00
const servers = [
( "tox.neuland.technology",
"15E9C309CFCB79FDDF0EBA057DABB49FE15F3803B1BFF06536AE2E5BA5E4690E".toPublicKey
)
]
2019-12-17 04:53:51 +01:00
for host, key in servers.items:
bot.bootstrap(host, key)
proc addAdmin(bot: Tox; id: Address) =
2019-12-25 15:40:09 +01:00
try:
discard bot.addFriend(
id, "You have been granted administrative rights to " & bot.name)
except ToxError:
discard
2019-12-17 04:53:51 +01:00
2019-12-23 19:29:15 +01:00
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"
2019-12-25 15:40:09 +01:00
type Command = enum
doorpass,
help,
invite,
lock,
2019-12-26 06:40:15 +01:00
readme,
revoke,
2019-12-25 15:40:09 +01:00
unlock,
proc setup(bot: Tox) =
2019-12-23 19:29:15 +01:00
let http = newAsyncHttpClient()
2019-12-25 15:40:09 +01:00
addTimer(20*1000, oneshot = false) do (fd: AsyncFD) -> bool:
2019-12-23 19:29:15 +01:00
asyncCheck updateStatus(bot, http)
let schalterClient = newAsyncHttpClient()
2019-12-25 15:40:09 +01:00
schalterClient.headers = newHttpHeaders()
2019-12-17 04:53:51 +01:00
2019-12-25 15:40:09 +01:00
let conference = bot.newConference()
`title=`(bot, conference, "C3D2")
bot.onFriendConnectionStatus do (f: Friend; status: Connection):
if status != TOX_CONNECTION_NONE:
bot.invite(f, conference)
2019-12-17 04:53:51 +01:00
2019-12-26 06:40:15 +01:00
bot.onFriendReadReceipt do (f: friend; id; MessageId):
discard """TODO some commands should be defered until a read receipt is acquired. Maybe just revoke."""
2019-12-23 18:50:36 +01:00
bot.onFriendMessage do (f: Friend; msg: string; kind: MessageType):
2019-12-25 15:40:09 +01:00
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.""")
2019-12-26 06:40:15 +01:00
of readme:
reply """Return bot README"""
of revoke:
reply """Remove yourself from the bot roster."""
2019-12-25 15:40:09 +01:00
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)
2019-12-26 06:40:15 +01:00
of readme:
reply readmeText
of revoke:
reply """Tchuss"""
discard bot.delete(f)
2019-12-25 15:40:09 +01:00
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())
2019-12-17 04:53:51 +01:00
proc newBot(name: string): Tox =
result = newTox do (opts: Options):
opts.localDiscoveryEnabled = true
opts.ipv6Enabled = true
2019-12-25 15:40:09 +01:00
if existsFile saveFileName:
opts.saveDataType = TOX_SAVEDATA_TYPE_TOX_SAVE
opts.savedata = readFile(saveFileName)
2019-12-17 04:53:51 +01:00
result.name = name
2019-12-25 15:40:09 +01:00
result.setup()
for id in adminIds: result.addAdmin(id)
2019-12-17 04:53:51 +01:00
result.bootstrap()
echo result.name, " is at ", result.address
proc main() =
2019-12-23 19:29:15 +01:00
let bot = newBot("HQ Bot")
2019-12-25 15:40:09 +01:00
writeFile(saveFileName, bot.saveData)
echo "DHT port and key: ", bot.udpPort, " ", bot.dhtId
2019-12-17 04:53:51 +01:00
while true:
iterate bot
poll bot.iterationInterval
main()