Browse Source

Implement avatar status image

master
Emery Hemingway 10 months ago
parent
commit
fb7359d89d
2 changed files with 56 additions and 17 deletions
  1. 1
    0
      .gitignore
  2. 55
    17
      src/hqtoxbot.nim

+ 1
- 0
.gitignore View File

@@ -1,3 +1,4 @@
1 1
 hqtoxbot
2 2
 result
3 3
 hqtoxbot.save
4
+avatar.png

+ 55
- 17
src/hqtoxbot.nim View File

@@ -1,21 +1,27 @@
1 1
 import toxcore
2 2
 
3
-import std/asyncdispatch, std/base64, std/json, std/httpclient, std/os, std/strutils
3
+import std/asyncdispatch, std/asyncfile, std/base64, std/json, std/httpclient,
4
+    std/os, std/strutils, std/uri
4 5
 
5 6
 {.passL: "-lcrypto".}
6 7
 
7 8
 const
8 9
   readmeText = readFile "README.md"
9 10
   spaceApiUrl = "http://spaceapi.hq.c3d2.de:3000/spaceapi.json"
11
+  shalterLockUrl = "http://172.22.99.204/door/lock"
12
+  shalterUnlockUrl = "http://172.22.99.204/door/unlock"
13
+    # These are IP addresses so the door can be unlocked without DNS
10 14
   saveFileName = "hqtoxbot.save"
15
+  iconFilename = "avatar.png"
11 16
   adminIds = [
12 17
     toAddress "DF0AC9107E0A30E7201C6832B017AC836FBD1EDAC390EE99B68625D73C3FD929FB47F1872CA4"
13 18
     # Emery
14 19
   ]
15 20
 
21
+
16 22
 proc bootstrap(bot: Tox) =
17 23
   const servers = [
18
-    ( "tox.neuland.technology",
24
+    ("tox.neuland.technology",
19 25
       "15E9C309CFCB79FDDF0EBA057DABB49FE15F3803B1BFF06536AE2E5BA5E4690E".toPublicKey
20 26
     )
21 27
   ]
@@ -29,16 +35,48 @@ proc addAdmin(bot: Tox; id: Address) =
29 35
   except ToxError:
30 36
     discard
31 37
 
32
-proc updateStatus(bot: Tox; http: AsyncHttpClient) {.async.} =
38
+proc sendAvatar(bot: Tox; friend: Friend) =
39
+  discard bot.send(
40
+    friend, TOX_FILE_KIND_AVATAR.uint32,
41
+    iconFilename.getFileSize, iconFilename)
42
+
43
+proc sendAvatarChunk(bot: Tox; friend: Friend; file: FileTransfer; pos: uint64;
44
+    size: int) {.async.} =
45
+  let iconFile = openAsync(iconFilename, fmRead)
46
+  iconFile.setFilePos(pos.int64);
47
+  let chunk = await iconFile.read(size)
48
+  close iconFile
49
+  bot.sendChunk(friend, file, pos, chunk)
50
+
51
+proc updateAvatar(bot: Tox) =
52
+  assert(iconFilename != "")
53
+  for friend in bot.friends:
54
+    if bot.connectionStatus(friend) != TOX_CONNECTION_NONE:
55
+      bot.sendAvatar(friend)
56
+
57
+proc updateStatus(bot: Tox) {.async.} =
33 58
   try:
34 59
     let
60
+      http = newAsyncHttpClient()
35 61
       rsp = await http.get(spaceApiUrl)
36 62
       body = await rsp.body
37 63
       space = parseJson body
38
-      status = $(space["status"])
64
+      status = space["status"].getStr
65
+      open = space["state"]["open"].getBool
39 66
     if bot.statusMessage != status:
40
-      bot.statusMessage = unescape $(space["status"])
67
+      bot.statusMessage = status
68
+      if open:
69
+        bot.status = TOX_USER_STATUS_NONE
70
+        let iconUrl = space["icon"]["open"].getStr.parseUri
71
+        await http.downloadFile($iconUrl, iconFilename)
72
+      else:
73
+        bot.status = TOX_USER_STATUS_AWAY
74
+        let iconUrl = space["icon"]["closed"].getStr.parseUri
75
+        await http.downloadFile($iconUrl, iconFilename)
76
+      bot.updateAvatar()
77
+    close http
41 78
   except:
79
+    echo getCurrentExceptionMsg()
42 80
     bot.statusMessage = "status update failed"
43 81
 
44 82
 type Command = enum
@@ -48,12 +86,11 @@ type Command = enum
48 86
   lock,
49 87
   readme,
50 88
   revoke,
51
-  unlock,
89
+  unlock
52 90
 
53 91
 proc setup(bot: Tox) =
54
-  let http = newAsyncHttpClient()
55 92
   addTimer(20*1000, oneshot = false) do (fd: AsyncFD) -> bool:
56
-    asyncCheck updateStatus(bot, http)
93
+    asyncCheck updateStatus(bot)
57 94
 
58 95
   let schalterClient = newAsyncHttpClient()
59 96
   schalterClient.headers = newHttpHeaders()
@@ -64,9 +101,7 @@ proc setup(bot: Tox) =
64 101
   bot.onFriendConnectionStatus do (f: Friend; status: Connection):
65 102
     if status != TOX_CONNECTION_NONE:
66 103
       bot.invite(f, conference)
67
-
68
-  bot.onFriendReadReceipt do (f: friend; id; MessageId):
69
-    discard """TODO some commands should be defered until a read receipt is acquired. Maybe just revoke."""
104
+      bot.sendAvatar(f)
70 105
 
71 106
   bot.onFriendMessage do (f: Friend; msg: string; kind: MessageType):
72 107
     proc reply(msg: string) =
@@ -114,16 +149,15 @@ proc setup(bot: Tox) =
114 149
       of invite:
115 150
         for id in words[1..words.high]:
116 151
           try:
117
-            let
118
-              address = id.toAddress
119
-              other = bot.addFriend(address, "You have been invited to the $1 by $2 ($3)" % [ bot.name, bot.name(f), $bot.publicKey(f) ])
120
-            bot.invite(other, conference)
152
+            discard bot.addFriend(id.toAddress,
153
+                "You have been invited to the $1 by $2 ($3)" % [bot.name,
154
+                bot.name(f), $bot.publicKey(f)])
121 155
           except:
122 156
             reply(getCurrentExceptionMsg())
123 157
 
124 158
       of lock:
125 159
         bot.typing(f, true)
126
-        let fut = schalterClient.post("http://schalter.hq.c3d2.de/door/lock")
160
+        let fut = schalterClient.post(shalterLockUrl)
127 161
         fut.addCallback do ():
128 162
           bot.typing(f, false)
129 163
           if fut.failed:
@@ -140,7 +174,7 @@ proc setup(bot: Tox) =
140 174
 
141 175
       of unlock:
142 176
         bot.typing(f, true)
143
-        let fut = schalterClient.post("http://schalter.hq.c3d2.de/door/unlock")
177
+        let fut = schalterClient.post(shalterUnlockUrl)
144 178
         fut.addCallback do ():
145 179
           bot.typing(f, false)
146 180
           if fut.failed:
@@ -150,6 +184,10 @@ proc setup(bot: Tox) =
150 184
     except:
151 185
       reply(getCurrentExceptionMsg())
152 186
 
187
+  bot.onFileChunkRequest do (friend: Friend; file: FileTransfer; pos: uint64; size: int):
188
+    if size != 0:
189
+      asyncCheck bot.sendAvatarChunk(friend, file, pos, size)
190
+
153 191
 proc newBot(name: string): Tox =
154 192
   result = newTox do (opts: Options):
155 193
     opts.localDiscoveryEnabled = true

Loading…
Cancel
Save