-
Notifications
You must be signed in to change notification settings - Fork 5
/
gitter-tools.red
316 lines (287 loc) · 6.81 KB
/
gitter-tools.red
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
Red []
do %gitter-api.red
fix-mold: func [
"Do some changes to texts in messages block, so mold wouldn't produce garbage"
value
/only
][
forall value [
replace/all value/1/text #"{" "^^{"
replace/all value/1/text #"}" "^^}"
replace/all value/1/html #"{" "^^{"
replace/all value/1/html #"}" "^^}"
]
either only [mold/only value][mold value]
]
select-by: function [
"Select map! or object! in series by it's field"
series
word
value
] [
foreach item series [
if equal? item/:word value [return item]
]
none
]
empty-block?: func [value][all [block? value empty? value]]
init-gitter: does [
do load %options.red
user: gitter/user-info
rooms: gitter/user-rooms user/id
room: select-room rooms "red/red" ; Remove later
]
select-room: function [
"Selects room by its name. Returns room ID."
rooms "Block of rooms"
name "Name of room"
/by "Select room by different field than name"
field
] [
unless by [field: 'name]
foreach room rooms [if equal? name room/:field [return room]]
none
]
list-rooms: function [
rooms
] [
foreach room rooms [
print room/name
]
]
get-message-author: func [message][
any [
; FIXME: There is NONE in message, but as a word!
; How that's possible?
all [message/fromUser message/fromUser <> to word! "none" message/fromUser/username]
message/author
]
]
strip-message: function [
"Remove unnecessary informations from message"
message
] [
message/html: none
message/author: get-message-author message
message/fromUser: none
if empty-block? message/mentions [message/mentions: none]
if empty-block? message/urls [message/urls: none]
if empty-block? message/issues [message/issues: none]
message/unread: none
message/readBy: none
message/meta: none
]
; TODO: split this in multiple functions
download-room: func [
room ; TODO support ID here and better conversion of different types
/compact "Remove some unnecessary fields"
/force "Do not use cached messages and re-download everything"
/verbose "Inform what is going on"
/to
filename
/with
cache [block!] "If we have messages in memory, we can save lot of time"
/local info ret last-id newest messages t
] [
ret: copy []
; some preparation
info: func [value /no-line] [if verbose [either no-line [prin value][print value]]]
info/no-line ["^/Download messages for room" form room/name "..."]
if path? room [room: gitter/get-room-info room]
unless exists? %messages/ [make-dir %messages/] ; TODO: This shouldn't be here
unless to [
filename: rejoin [%messages/ room/id %.red]
]
info/no-line ["^/Download messages for room" room/name "..."]
; load cached messages, when required
either with [
ret: cache
][
info ["Checking cached file" filename]
if all [not force exists? filename] [
t: now/time/precise
info/no-line ["Loading file" filename "..."]
ret: append copy [] load filename
info [length? ret "messages loaded in" now/time/precise - t]
]
]
either empty? ret [
info "Downloading all messages"
; we have no messages, so we will downloaded them
; from newest to oldest (that's how Gitter works)
; load first bunch of messages
ret: gitter/get-messages room
if empty? ret [
; the room is empty
write filename ""
return none
]
if compact [foreach message ret [strip-message message]]
last-id: ret/1/id
write filename mold/only ret
until [
info/no-line ["Downloading messages before" ret/1/sent "..."]
ret: gitter/get-messages/with room [beforeId: last-id]
info [length? ret "messages downloaded."]
if compact [foreach message ret [strip-message message]]
unless empty? ret [
last-id: ret/1/id
; FIXME: This is workaround for missing MOLD/ALL
; "{" and "}" are not escaped and can't be loaded back
; so we're going to escape them manually
write/append filename mold/only reverse ret
ret: reverse ret
]
empty? ret
]
ret
] [
; we have cached messages, so we will download only newer messages
; now we will download messages in loop until we have all new messages
until [
info/no-line ["Downloading messages posted after" ret/1/sent "..."]
; NOTE: [afterId] returns messages from oldest to newest, so we need
; to reverse the order, to have same format as cached messages
messages: reverse gitter/get-messages/with
room
compose [afterId: (ret/1/id)]
info [length? messages "messages downloaded."]
if compact [foreach message ret [strip-message message]]
insert ret messages ; we may be inserting empty block, but who cares
empty? messages
]
; now save everything (there's no write/insert do do it in loop)
write filename mold ret
ret
]
]
select-message: function [
"Select message by id"
messages
id
] [
foreach message messages [
if equal? id message/id [return message]
]
none
]
; --- searching
question?: function [
"Return LOGIC! value indicating whether message contains question mark."
message
] [
not not find message/text #"?"
]
get-mentions: function [
message
][
name: none
mentions: copy []
parse message/text [
some [
"@" copy name to space (append mentions name)
| skip
]
]
mentions
]
get-code: function [
"Returns block of code snippets or NONE"
message
] [
code: make block! 4
fence: "```"
parse message/text [
some [
thru fence
copy value
to fence
3 skip
(append code value)
]
]
either empty? code [none] [code]
]
get-all-code: function [
messages
] [
code: make map! []
foreach message messages [
code/(message/id): get-code message
]
code
]
gfind: func [
messages
string
] [
result: copy []
foreach message messages [
if find message/text string [append result message]
]
result
]
; ---
get-user-messages: function [
messages
] [
users: #()
template: #(
posts: 0
total-chars: 0
messages: []
)
foreach message messages [
user: message/fromUser/username
unless users/:user [
users/:user: copy/deep template
]
users/:user/posts: users/:user/posts + 1
users/:user/total-chars: users/:user/total-chars + length? message/text
append users/:user/messages message
]
users
]
maximum-of: function [
series
] [
max: first series
pos: 1
forall next series [
if series/1 > max [
max: series/1
pos: index? series
]
]
at series pos
]
red-group-id: "57542d9cc43b8c601977e621"
get-rooms: func [
"Save room info on disk for later usage"
group
/local rooms
][
rooms: gitter/group-rooms group
unless exists? %rooms/ [make-dir %rooms/]
foreach room rooms [
save rejoin [%rooms/ room/id %.red] room
]
]
probe-messages: function [
messages
] [
foreach message messages [
print rejoin [get-message-author message " (" message/sent ") wrote:"]
print "---------------------------------------------------------------"
print message/text
print "---------------------------------------------------------------"
print ""
]
]
probe-rooms: function [
rooms
] [
foreach room rooms [
print rejoin [room/name ": #" room/id]
]
]