├── .editorconfig
├── .gitignore
├── .travis.yml
├── GUIDE.md
├── LICENSE
├── README.md
├── docs
├── BaseEntity.html
├── Context.html
├── DogeEntity.html
├── DogehouseCr.html
├── DogehouseCr
│ ├── Client.html
│ ├── Message.html
│ ├── Room.html
│ └── User.html
├── Galileo.html
├── Galileo
│ ├── Client.html
│ ├── Message.html
│ ├── Room.html
│ └── User.html
├── Room.html
├── css
│ └── style.css
├── index.html
├── index.json
├── js
│ └── doc.js
├── search-index.js
└── toplevel.html
├── examples
├── on_message.cr
├── on_ready.cr
├── on_room_join.cr
└── on_user_joined_room.cr
├── shard.yml
├── spec
├── galileo_spec.cr
└── spec_helper.cr
└── src
├── galileo.cr
└── galileo
├── client.cr
├── message.cr
├── ops.cr
├── room.cr
└── user.cr
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*.cr]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 2
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /lib/
2 | /bin/
3 | /.shards/
4 | *.dwarf
5 |
6 | # Libraries don't need dependency lock
7 | # Dependencies will be locked in applications that use them
8 | /shard.lock
9 | setupenv.fish
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: crystal
2 |
3 | # Uncomment the following if you'd like Travis to run specs and check code formatting
4 | # script:
5 | # - crystal spec
6 | # - crystal tool format --check
7 |
--------------------------------------------------------------------------------
/GUIDE.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | 1. Add the dependency to your `shard.yml`:
4 |
5 | ```yaml
6 | dependencies:
7 | galileo:
8 | github: willdoescode/galileo
9 | ```
10 |
11 | 2. Run `shards install`
12 |
13 | # Usage
14 |
15 | ```crystal
16 | require "galileo"
17 | ```
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2021 Will Lane
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
Galileo
3 | The fastest way to make the fastest dogehouse bots.
4 |
5 |
6 |
13 |
--------------------------------------------------------------------------------
/docs/BaseEntity.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | BaseEntity - dogehouse_cr main-dev
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
103 |
104 |
105 |
106 |
107 |
108 | class BaseEntity
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
134 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
155 |
156 |
157 | dogehouse_cr/entities/base.cr
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
178 |
179 |
180 |
181 | #join_room (roomId : String)
182 |
183 | Join room will join the room associated with a roomId
` client.join_room "roomid"
`
184 |
185 |
186 |
187 |
188 | #send_message (message : String)
189 |
190 | Sends message to whatever room currently in
` client.send_message "msg"
`
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 |
237 |
238 |
239 |
240 |
241 | def
join_room (roomId : String)
242 |
243 |
#
244 |
245 |
246 |
247 |
248 |
Join room will join the room associated with a roomId
249 |
250 |
client.join_room "roomid"
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 | def
send_message (message : String)
263 |
264 |
#
265 |
266 |
267 |
268 |
269 |
Sends message to whatever room currently in
270 |
271 |
client.send_message "msg"
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
--------------------------------------------------------------------------------
/docs/Context.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Context - dogehouse_cr main-dev
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
103 |
104 |
105 |
106 |
107 |
108 | class Context
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
139 |
140 |
141 | dogehouse_cr/entities/context.cr
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
158 |
166 |
167 |
168 |
169 |
170 |
171 |
179 |
192 |
193 |
194 |
195 |
196 |
197 |
244 |
245 |
246 |
254 |
255 |
256 |
257 |
258 | def self.
new (ws : HTTP::WebSocket)
259 |
260 |
#
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
282 |
283 |
284 |
285 |
286 | def
ws : HTTP::WebSocket
287 |
288 |
#
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 | def
ws= (ws : HTTP::WebSocket)
301 |
302 |
#
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
--------------------------------------------------------------------------------
/docs/DogeEntity.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | DogeEntity - dogehouse_cr main-dev
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
98 |
99 |
100 |
101 |
102 |
103 | class DogeEntity
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
129 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
150 |
151 |
152 | dogehouse_cr/entity.cr
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
173 |
174 |
175 |
176 | #join_room (roomId : String)
177 |
178 | Join room will join the room associated with a roomId
` client.join_room "roomid"
`
179 |
180 |
181 |
182 |
183 | #send_message (message : String)
184 |
185 | Sends message to whatever room currently in
` client.send_message "msg"
`
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 |
232 |
233 |
234 |
235 |
236 | def
join_room (roomId : String)
237 |
238 |
#
239 |
240 |
241 |
242 |
243 |
Join room will join the room associated with a roomId
244 |
245 |
client.join_room "roomid"
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 | def
send_message (message : String)
258 |
259 |
#
260 |
261 |
262 |
263 |
264 |
Sends message to whatever room currently in
265 |
266 |
client.send_message "msg"
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
--------------------------------------------------------------------------------
/docs/DogehouseCr.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | DogehouseCr - dogehouse_cr main-dev
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
93 |
94 |
95 |
96 |
97 |
98 | module DogehouseCr
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
127 |
128 |
129 | dogehouse_cr.cr
130 |
131 |
132 |
133 |
134 | dogehouse_cr/entities/message.cr
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
151 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
183 |
184 |
185 |
186 |
187 | def self.
new (token : String, refreshToken : String)
188 |
189 |
#
190 |
191 |
192 |
193 |
194 |
Create a new client with yoru token and refreshToken
195 |
196 |
client = DogehouseCr .new "token" , "refreshToken"
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
--------------------------------------------------------------------------------
/docs/DogehouseCr/Message.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | DogehouseCr::Message - dogehouse_cr main-dev
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
93 |
94 |
95 |
96 |
97 |
98 | class DogehouseCr::Message
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
129 |
130 |
131 | dogehouse_cr/entities/message.cr
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
148 |
156 |
157 |
158 |
159 |
160 |
161 |
169 |
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 |
230 |
231 |
232 |
233 |
234 | def self.
new (user_id : String, sent_at : String, is_whisper : Bool, content : String)
235 |
236 |
#
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
258 |
259 |
260 |
261 |
262 | def
content : String
263 |
264 |
#
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 | def
is_whisper : Bool
277 |
278 |
#
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 | def
sent_at : String
291 |
292 |
#
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 | def
user_id : String
305 |
306 |
#
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
--------------------------------------------------------------------------------
/docs/DogehouseCr/Room.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | DogehouseCr::Room - dogehouse_cr main-dev
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
93 |
94 |
95 |
96 |
97 |
98 | class DogehouseCr::Room
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
129 |
130 |
131 | dogehouse_cr/entities/room.cr
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
148 |
156 |
157 |
158 |
159 |
160 |
161 |
169 |
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 |
230 |
231 |
232 |
233 |
234 | def self.
new (id : String, name : String, description : String, is_private : Bool)
235 |
236 |
#
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
258 |
259 |
260 |
261 |
262 | def
description : String
263 |
264 |
#
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 | def
id : String
277 |
278 |
#
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 | def
is_private : Bool
291 |
292 |
#
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 | def
name : String
305 |
306 |
#
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
--------------------------------------------------------------------------------
/docs/DogehouseCr/User.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | DogehouseCr::User - dogehouse_cr main-dev
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
93 |
94 |
95 |
96 |
97 |
98 | class DogehouseCr::User
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
129 |
130 |
131 | dogehouse_cr/entities/user.cr
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
148 |
149 |
150 |
151 | .from_json (m : Hash(String, JSON::Any)) : User
152 |
153 |
154 |
155 |
156 | .new (id : String, username : String, avatar_url : String, banner_url : String, bio : String, online : Bool, staff : Bool, last_online : String, current_room_id : String, display_name : String, num_following : Int32, num_followers : Int32, contributions : Int32, you_are_following : Bool, follows_you : Bool, bot_owner_id : String)
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
174 |
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 |
301 |
302 |
303 |
304 |
305 | def self.
from_json (m : Hash(String, JSON::Any)) :
User
306 |
307 |
#
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 | def self.
new (id : String, username : String, avatar_url : String, banner_url : String, bio : String, online : Bool, staff : Bool, last_online : String, current_room_id : String, display_name : String, num_following : Int32, num_followers : Int32, contributions : Int32, you_are_following : Bool, follows_you : Bool, bot_owner_id : String)
320 |
321 |
#
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
343 |
344 |
345 |
346 |
347 | def
avatar_url : String
348 |
349 |
#
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 | def
banner_url : String
362 |
363 |
#
364 |
365 |
366 |
367 |
368 |
If there is no banner url given this will be an empty string
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 | def
bio : String
381 |
382 |
#
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 | def
bot_owner_id : String
395 |
396 |
#
397 |
398 |
399 |
400 |
401 |
If user is not a bot this will be an empty string
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 | def
contributions : Int32
414 |
415 |
#
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 | def
current_room_id : String
428 |
429 |
#
430 |
431 |
432 |
433 |
434 |
If there is no current room id given this will be an empty string
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 | def
display_name : String
447 |
448 |
#
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 | def
follows_you : Bool
461 |
462 |
#
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 | def
id : String
475 |
476 |
#
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 | def
last_online : String
489 |
490 |
#
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 | def
num_followers : Int32
503 |
504 |
#
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 | def
num_following : Int32
517 |
518 |
#
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 | def
online : Bool
531 |
532 |
#
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 | def
staff : Bool
545 |
546 |
#
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 | def
username : String
559 |
560 |
#
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 | def
you_are_following : Bool
573 |
574 |
#
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
--------------------------------------------------------------------------------
/docs/Galileo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Galileo - galileo main-dev
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
93 |
94 |
95 |
96 |
97 |
98 | module Galileo
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
127 |
128 |
129 | galileo.cr
130 |
131 |
132 |
133 |
134 | galileo/message.cr
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
151 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
183 |
184 |
185 |
186 |
187 | def self.
new (token : String, refreshToken : String)
188 |
189 |
#
190 |
191 |
192 |
193 |
194 |
Create a new client with yoru token and refreshToken
195 |
196 |
client = DogehouseCr .new "token" , "refreshToken"
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
--------------------------------------------------------------------------------
/docs/Galileo/Message.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Galileo::Message - galileo main-dev
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
93 |
94 |
95 |
96 |
97 |
98 | class Galileo::Message
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
129 |
130 |
131 | galileo/message.cr
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
148 |
156 |
157 |
158 |
159 |
167 |
180 |
181 |
182 |
183 |
191 |
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 |
252 |
253 |
254 |
255 |
256 | def self.
new (user_id : String, sent_at : String, is_whisper : Bool, content : String)
257 |
258 |
#
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
278 |
279 |
280 |
281 |
282 | def self.
decode (message : Array(Hash(String, JSON::Any))) : String
283 |
284 |
#
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 | def self.
encode (message : String)
297 |
298 |
#
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
318 |
319 |
320 |
321 |
322 | def
content : String
323 |
324 |
#
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 | def
is_whisper : Bool
337 |
338 |
#
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 | def
sent_at : String
351 |
352 |
#
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 | def
user_id : String
365 |
366 |
#
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
--------------------------------------------------------------------------------
/docs/Galileo/Room.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Galileo::Room - galileo main-dev
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
93 |
94 |
95 |
96 |
97 |
98 | class Galileo::Room
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
129 |
130 |
131 | galileo/room.cr
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
148 |
156 |
157 |
158 |
159 |
167 |
175 |
176 |
177 |
178 |
186 |
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 |
267 |
268 |
269 |
270 |
271 | def self.
new (id : String, name : String, description : String, is_private : Bool)
272 |
273 |
#
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
293 |
294 |
295 |
296 |
297 | def self.
from_json (payload : Hash(String, JSON::Any))
298 |
299 |
#
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
319 |
320 |
321 |
322 |
323 | def
description : String
324 |
325 |
#
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 | def
description= (description : String)
338 |
339 |
#
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 | def
id : String
352 |
353 |
#
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 | def
id= (id : String)
366 |
367 |
#
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 | def
is_private : Bool
380 |
381 |
#
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 | def
is_private= (is_private : Bool)
394 |
395 |
#
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 | def
name : String
408 |
409 |
#
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 | def
name= (name : String)
422 |
423 |
#
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
--------------------------------------------------------------------------------
/docs/Galileo/User.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Galileo::User - galileo main-dev
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
93 |
94 |
95 |
96 |
97 |
98 | class Galileo::User
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
129 |
130 |
131 | galileo/user.cr
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
148 |
149 |
150 |
151 | .from_json (m : Hash(String, JSON::Any)) : User
152 |
153 |
154 |
155 |
156 | .new (id : String, username : String, avatar_url : String, banner_url : String, bio : String, online : Bool, staff : Bool, last_online : String, current_room_id : String, display_name : String, num_following : Int32, num_followers : Int32, contributions : Int32, you_are_following : Bool, follows_you : Bool, bot_owner_id : String)
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
174 |
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 |
301 |
302 |
303 |
304 |
305 | def self.
from_json (m : Hash(String, JSON::Any)) :
User
306 |
307 |
#
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 | def self.
new (id : String, username : String, avatar_url : String, banner_url : String, bio : String, online : Bool, staff : Bool, last_online : String, current_room_id : String, display_name : String, num_following : Int32, num_followers : Int32, contributions : Int32, you_are_following : Bool, follows_you : Bool, bot_owner_id : String)
320 |
321 |
#
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
343 |
344 |
345 |
346 |
347 | def
avatar_url : String
348 |
349 |
#
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 | def
banner_url : String
362 |
363 |
#
364 |
365 |
366 |
367 |
368 |
If there is no banner url given this will be an empty string
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 | def
bio : String
381 |
382 |
#
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 | def
bot_owner_id : String
395 |
396 |
#
397 |
398 |
399 |
400 |
401 |
If user is not a bot this will be an empty string
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 | def
contributions : Int32
414 |
415 |
#
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 | def
current_room_id : String
428 |
429 |
#
430 |
431 |
432 |
433 |
434 |
If there is no current room id given this will be an empty string
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 | def
display_name : String
447 |
448 |
#
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 | def
follows_you : Bool
461 |
462 |
#
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 | def
id : String
475 |
476 |
#
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 | def
last_online : String
489 |
490 |
#
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 | def
num_followers : Int32
503 |
504 |
#
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 | def
num_following : Int32
517 |
518 |
#
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 | def
online : Bool
531 |
532 |
#
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 | def
staff : Bool
545 |
546 |
#
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 | def
username : String
559 |
560 |
#
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 | def
you_are_following : Bool
573 |
574 |
#
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
--------------------------------------------------------------------------------
/docs/Room.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Room - dogehouse_cr main-dev
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
98 |
99 |
100 |
101 |
102 |
103 | class Room
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
134 |
135 |
136 | dogehouse_cr/entity.cr
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
153 |
161 |
162 |
163 |
164 |
165 |
166 |
174 |
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 |
235 |
236 |
237 |
238 |
239 | def self.
new (id : String, name : String, description : String, is_private : Bool)
240 |
241 |
#
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
263 |
264 |
265 |
266 |
267 | def
description : String
268 |
269 |
#
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 | def
id : String
282 |
283 |
#
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 | def
is_private : Bool
296 |
297 |
#
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 | def
name : String
310 |
311 |
#
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
--------------------------------------------------------------------------------
/docs/css/style.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | background: #FFFFFF;
3 | position: relative;
4 | margin: 0;
5 | padding: 0;
6 | width: 100%;
7 | height: 100%;
8 | overflow: hidden;
9 | }
10 |
11 | body {
12 | font-family: "Avenir", "Tahoma", "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif;
13 | color: #333;
14 | line-height: 1.5;
15 | }
16 |
17 | a {
18 | color: #263F6C;
19 | }
20 |
21 | a:visited {
22 | color: #112750;
23 | }
24 |
25 | h1, h2, h3, h4, h5, h6 {
26 | margin: 35px 0 25px;
27 | color: #444444;
28 | }
29 |
30 | h1.type-name {
31 | color: #47266E;
32 | margin: 20px 0 30px;
33 | background-color: #F8F8F8;
34 | padding: 10px 12px;
35 | border: 1px solid #EBEBEB;
36 | border-radius: 2px;
37 | }
38 |
39 | h2 {
40 | border-bottom: 1px solid #E6E6E6;
41 | padding-bottom: 5px;
42 | }
43 |
44 | body {
45 | display: flex;
46 | }
47 |
48 | .sidebar, .main-content {
49 | overflow: auto;
50 | }
51 |
52 | .sidebar {
53 | width: 30em;
54 | color: #F8F4FD;
55 | background-color: #2E1052;
56 | padding: 0 0 30px;
57 | box-shadow: inset -3px 0 4px rgba(0,0,0,.35);
58 | line-height: 1.2;
59 | z-index: 0;
60 | }
61 |
62 | .sidebar .search-box {
63 | padding: 13px 9px;
64 | }
65 |
66 | .sidebar input {
67 | display: block;
68 | box-sizing: border-box;
69 | margin: 0;
70 | padding: 5px;
71 | font: inherit;
72 | font-family: inherit;
73 | line-height: 1.2;
74 | width: 100%;
75 | border: 0;
76 | outline: 0;
77 | border-radius: 2px;
78 | box-shadow: 0px 3px 5px rgba(0,0,0,.25);
79 | transition: box-shadow .12s;
80 | }
81 |
82 | .sidebar input:focus {
83 | box-shadow: 0px 5px 6px rgba(0,0,0,.5);
84 | }
85 |
86 | .sidebar input::-webkit-input-placeholder { /* Chrome/Opera/Safari */
87 | color: #C8C8C8;
88 | font-size: 14px;
89 | text-indent: 2px;
90 | }
91 |
92 | .sidebar input::-moz-placeholder { /* Firefox 19+ */
93 | color: #C8C8C8;
94 | font-size: 14px;
95 | text-indent: 2px;
96 | }
97 |
98 | .sidebar input:-ms-input-placeholder { /* IE 10+ */
99 | color: #C8C8C8;
100 | font-size: 14px;
101 | text-indent: 2px;
102 | }
103 |
104 | .sidebar input:-moz-placeholder { /* Firefox 18- */
105 | color: #C8C8C8;
106 | font-size: 14px;
107 | text-indent: 2px;
108 | }
109 |
110 | .project-summary {
111 | padding: 9px 15px 30px 30px;
112 | }
113 |
114 | .project-name {
115 | font-size: 1.4rem;
116 | margin: 0;
117 | color: #f4f4f4;
118 | font-weight: 600;
119 | }
120 |
121 | .project-version {
122 | margin-top: 5px;
123 | display: inline-block;
124 | position: relative;
125 | }
126 |
127 | .project-version > form::after {
128 | position: absolute;
129 | right: 0;
130 | top: 0;
131 | content: "\25BC";
132 | font-size: .6em;
133 | line-height: 1.2rem;
134 | z-index: -1;
135 | }
136 |
137 | .project-versions-nav {
138 | cursor: pointer;
139 | margin: 0;
140 | padding: 0 .9em 0 0;
141 | border: none;
142 | -moz-appearance: none;
143 | -webkit-appearance: none;
144 | appearance: none;
145 | background-color: transparent;
146 | color: inherit;
147 | font-family: inherit;
148 | font-size: inherit;
149 | line-height: inherit;
150 | }
151 | .project-versions-nav:focus {
152 | outline: none;
153 | }
154 |
155 | .project-versions-nav > option {
156 | color: initial;
157 | }
158 |
159 | .sidebar ul {
160 | margin: 0;
161 | padding: 0;
162 | list-style: none outside;
163 | }
164 |
165 | .sidebar li {
166 | display: block;
167 | position: relative;
168 | }
169 |
170 | .types-list li.hide {
171 | display: none;
172 | }
173 |
174 | .sidebar a {
175 | text-decoration: none;
176 | color: inherit;
177 | transition: color .14s;
178 | }
179 | .types-list a {
180 | display: block;
181 | padding: 5px 15px 5px 30px;
182 | }
183 |
184 | .types-list {
185 | display: block;
186 | }
187 |
188 | .sidebar a:focus {
189 | outline: 1px solid #D1B7F1;
190 | }
191 |
192 | .types-list a {
193 | padding: 5px 15px 5px 30px;
194 | }
195 |
196 | .sidebar .current > a,
197 | .sidebar a:hover {
198 | color: #866BA6;
199 | }
200 |
201 | .types-list li ul {
202 | overflow: hidden;
203 | height: 0;
204 | max-height: 0;
205 | transition: 1s ease-in-out;
206 | }
207 |
208 | .types-list li.parent {
209 | padding-left: 30px;
210 | }
211 |
212 | .types-list li.parent::before {
213 | box-sizing: border-box;
214 | content: "▼";
215 | display: block;
216 | width: 30px;
217 | height: 30px;
218 | position: absolute;
219 | top: 0;
220 | left: 0;
221 | text-align: center;
222 | color: white;
223 | font-size: 8px;
224 | line-height: 30px;
225 | transform: rotateZ(-90deg);
226 | cursor: pointer;
227 | transition: .2s linear;
228 | }
229 |
230 |
231 | .types-list li.parent > a {
232 | padding-left: 0;
233 | }
234 |
235 | .types-list li.parent.open::before {
236 | transform: rotateZ(0);
237 | }
238 |
239 | .types-list li.open > ul {
240 | height: auto;
241 | max-height: 1000em;
242 | }
243 |
244 | .main-content {
245 | padding: 0 30px 30px 30px;
246 | width: 100%;
247 | }
248 |
249 | .kind {
250 | font-size: 60%;
251 | color: #866BA6;
252 | }
253 |
254 | .superclass-hierarchy {
255 | margin: -15px 0 30px 0;
256 | padding: 0;
257 | list-style: none outside;
258 | font-size: 80%;
259 | }
260 |
261 | .superclass-hierarchy .superclass {
262 | display: inline-block;
263 | margin: 0 7px 0 0;
264 | padding: 0;
265 | }
266 |
267 | .superclass-hierarchy .superclass + .superclass::before {
268 | content: "<";
269 | margin-right: 7px;
270 | }
271 |
272 | .other-types-list li {
273 | display: inline-block;
274 | }
275 |
276 | .other-types-list,
277 | .list-summary {
278 | margin: 0 0 30px 0;
279 | padding: 0;
280 | list-style: none outside;
281 | }
282 |
283 | .entry-const {
284 | font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace;
285 | }
286 |
287 | .entry-const code {
288 | white-space: pre-wrap;
289 | }
290 |
291 | .entry-summary {
292 | padding-bottom: 4px;
293 | }
294 |
295 | .superclass-hierarchy .superclass a,
296 | .other-type a,
297 | .entry-summary .signature {
298 | padding: 4px 8px;
299 | margin-bottom: 4px;
300 | display: inline-block;
301 | background-color: #f8f8f8;
302 | color: #47266E;
303 | border: 1px solid #f0f0f0;
304 | text-decoration: none;
305 | border-radius: 3px;
306 | font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace;
307 | transition: background .15s, border-color .15s;
308 | }
309 |
310 | .superclass-hierarchy .superclass a:hover,
311 | .other-type a:hover,
312 | .entry-summary .signature:hover {
313 | background: #D5CAE3;
314 | border-color: #624288;
315 | }
316 |
317 | .entry-summary .summary {
318 | padding-left: 32px;
319 | }
320 |
321 | .entry-summary .summary p {
322 | margin: 12px 0 16px;
323 | }
324 |
325 | .entry-summary a {
326 | text-decoration: none;
327 | }
328 |
329 | .entry-detail {
330 | padding: 30px 0;
331 | }
332 |
333 | .entry-detail .signature {
334 | position: relative;
335 | padding: 5px 15px;
336 | margin-bottom: 10px;
337 | display: block;
338 | border-radius: 5px;
339 | background-color: #f8f8f8;
340 | color: #47266E;
341 | border: 1px solid #f0f0f0;
342 | font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace;
343 | transition: .2s ease-in-out;
344 | }
345 |
346 | .entry-detail:target .signature {
347 | background-color: #D5CAE3;
348 | border: 1px solid #624288;
349 | }
350 |
351 | .entry-detail .signature .method-permalink {
352 | position: absolute;
353 | top: 0;
354 | left: -35px;
355 | padding: 5px 15px;
356 | text-decoration: none;
357 | font-weight: bold;
358 | color: #624288;
359 | opacity: .4;
360 | transition: opacity .2s;
361 | }
362 |
363 | .entry-detail .signature .method-permalink:hover {
364 | opacity: 1;
365 | }
366 |
367 | .entry-detail:target .signature .method-permalink {
368 | opacity: 1;
369 | }
370 |
371 | .methods-inherited {
372 | padding-right: 10%;
373 | line-height: 1.5em;
374 | }
375 |
376 | .methods-inherited h3 {
377 | margin-bottom: 4px;
378 | }
379 |
380 | .methods-inherited a {
381 | display: inline-block;
382 | text-decoration: none;
383 | color: #47266E;
384 | }
385 |
386 | .methods-inherited a:hover {
387 | text-decoration: underline;
388 | color: #6C518B;
389 | }
390 |
391 | .methods-inherited .tooltip>span {
392 | background: #D5CAE3;
393 | padding: 4px 8px;
394 | border-radius: 3px;
395 | margin: -4px -8px;
396 | }
397 |
398 | .methods-inherited .tooltip * {
399 | color: #47266E;
400 | }
401 |
402 | pre {
403 | padding: 10px 20px;
404 | margin-top: 4px;
405 | border-radius: 3px;
406 | line-height: 1.45;
407 | overflow: auto;
408 | color: #333;
409 | background: #fdfdfd;
410 | font-size: 14px;
411 | border: 1px solid #eee;
412 | }
413 |
414 | code {
415 | font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace;
416 | }
417 |
418 | :not(pre) > code {
419 | background-color: rgba(40,35,30,0.05);
420 | padding: 0.2em 0.4em;
421 | font-size: 85%;
422 | border-radius: 3px;
423 | }
424 |
425 | span.flag {
426 | padding: 2px 4px 1px;
427 | border-radius: 3px;
428 | margin-right: 3px;
429 | font-size: 11px;
430 | border: 1px solid transparent;
431 | }
432 |
433 | span.flag.orange {
434 | background-color: #EE8737;
435 | color: #FCEBDD;
436 | border-color: #EB7317;
437 | }
438 |
439 | span.flag.yellow {
440 | background-color: #E4B91C;
441 | color: #FCF8E8;
442 | border-color: #B69115;
443 | }
444 |
445 | span.flag.green {
446 | background-color: #469C14;
447 | color: #E2F9D3;
448 | border-color: #34700E;
449 | }
450 |
451 | span.flag.red {
452 | background-color: #BF1919;
453 | color: #F9ECEC;
454 | border-color: #822C2C;
455 | }
456 |
457 | span.flag.purple {
458 | background-color: #2E1052;
459 | color: #ECE1F9;
460 | border-color: #1F0B37;
461 | }
462 |
463 | span.flag.lime {
464 | background-color: #a3ff00;
465 | color: #222222;
466 | border-color: #00ff1e;
467 | }
468 |
469 | .tooltip>span {
470 | position: absolute;
471 | opacity: 0;
472 | display: none;
473 | pointer-events: none;
474 | }
475 |
476 | .tooltip:hover>span {
477 | display: inline-block;
478 | opacity: 1;
479 | }
480 |
481 | .c {
482 | color: #969896;
483 | }
484 |
485 | .n {
486 | color: #0086b3;
487 | }
488 |
489 | .t {
490 | color: #0086b3;
491 | }
492 |
493 | .s {
494 | color: #183691;
495 | }
496 |
497 | .i {
498 | color: #7f5030;
499 | }
500 |
501 | .k {
502 | color: #a71d5d;
503 | }
504 |
505 | .o {
506 | color: #a71d5d;
507 | }
508 |
509 | .m {
510 | color: #795da3;
511 | }
512 |
513 | .hidden {
514 | display: none;
515 | }
516 | .search-results {
517 | font-size: 90%;
518 | line-height: 1.3;
519 | }
520 |
521 | .search-results mark {
522 | color: inherit;
523 | background: transparent;
524 | font-weight: bold;
525 | }
526 | .search-result {
527 | padding: 5px 8px 5px 5px;
528 | cursor: pointer;
529 | border-left: 5px solid transparent;
530 | transform: translateX(-3px);
531 | transition: all .2s, background-color 0s, border .02s;
532 | min-height: 3.2em;
533 | }
534 | .search-result.current {
535 | border-left-color: #ddd;
536 | background-color: rgba(200,200,200,0.4);
537 | transform: translateX(0);
538 | transition: all .2s, background-color .5s, border 0s;
539 | }
540 | .search-result.current:hover,
541 | .search-result.current:focus {
542 | border-left-color: #866BA6;
543 | }
544 | .search-result:not(.current):nth-child(2n) {
545 | background-color: rgba(255,255,255,.06);
546 | }
547 | .search-result__title {
548 | font-size: 105%;
549 | word-break: break-all;
550 | line-height: 1.1;
551 | padding: 3px 0;
552 | }
553 | .search-result__title strong {
554 | font-weight: normal;
555 | }
556 | .search-results .search-result__title > a {
557 | padding: 0;
558 | display: block;
559 | }
560 | .search-result__title > a > .args {
561 | color: #dddddd;
562 | font-weight: 300;
563 | transition: inherit;
564 | font-size: 88%;
565 | line-height: 1.2;
566 | letter-spacing: -.02em;
567 | }
568 | .search-result__title > a > .args * {
569 | color: inherit;
570 | }
571 |
572 | .search-result a,
573 | .search-result a:hover {
574 | color: inherit;
575 | }
576 | .search-result:not(.current):hover .search-result__title > a,
577 | .search-result:not(.current):focus .search-result__title > a,
578 | .search-result__title > a:focus {
579 | color: #866BA6;
580 | }
581 | .search-result:not(.current):hover .args,
582 | .search-result:not(.current):focus .args {
583 | color: #6a5a7d;
584 | }
585 |
586 | .search-result__type {
587 | color: #e8e8e8;
588 | font-weight: 300;
589 | }
590 | .search-result__doc {
591 | color: #bbbbbb;
592 | font-size: 90%;
593 | }
594 | .search-result__doc p {
595 | margin: 0;
596 | text-overflow: ellipsis;
597 | display: -webkit-box;
598 | -webkit-box-orient: vertical;
599 | -webkit-line-clamp: 2;
600 | overflow: hidden;
601 | line-height: 1.2em;
602 | max-height: 2.4em;
603 | }
604 |
605 | .js-modal-visible .modal-background {
606 | display: flex;
607 | }
608 | .main-content {
609 | position: relative;
610 | }
611 | .modal-background {
612 | position: absolute;
613 | display: none;
614 | height: 100%;
615 | width: 100%;
616 | background: rgba(120,120,120,.4);
617 | z-index: 100;
618 | align-items: center;
619 | justify-content: center;
620 | }
621 | .usage-modal {
622 | max-width: 90%;
623 | background: #fff;
624 | border: 2px solid #ccc;
625 | border-radius: 9px;
626 | padding: 5px 15px 20px;
627 | min-width: 50%;
628 | color: #555;
629 | position: relative;
630 | transform: scale(.5);
631 | transition: transform 200ms;
632 | }
633 | .js-modal-visible .usage-modal {
634 | transform: scale(1);
635 | }
636 | .usage-modal > .close-button {
637 | position: absolute;
638 | right: 15px;
639 | top: 8px;
640 | color: #aaa;
641 | font-size: 27px;
642 | cursor: pointer;
643 | }
644 | .usage-modal > .close-button:hover {
645 | text-shadow: 2px 2px 2px #ccc;
646 | color: #999;
647 | }
648 | .modal-title {
649 | margin: 0;
650 | text-align: center;
651 | font-weight: normal;
652 | color: #666;
653 | border-bottom: 2px solid #ddd;
654 | padding: 10px;
655 | }
656 | .usage-list {
657 | padding: 0;
658 | margin: 13px;
659 | }
660 | .usage-list > li {
661 | padding: 5px 2px;
662 | overflow: auto;
663 | padding-left: 100px;
664 | min-width: 12em;
665 | }
666 | .usage-modal kbd {
667 | background: #eee;
668 | border: 1px solid #ccc;
669 | border-bottom-width: 2px;
670 | border-radius: 3px;
671 | padding: 3px 8px;
672 | font-family: monospace;
673 | margin-right: 2px;
674 | display: inline-block;
675 | }
676 | .usage-key {
677 | float: left;
678 | clear: left;
679 | margin-left: -100px;
680 | margin-right: 12px;
681 | }
682 | .doc-inherited {
683 | font-weight: bold;
684 | }
685 |
686 | .anchor {
687 | float: left;
688 | padding-right: 4px;
689 | margin-left: -20px;
690 | }
691 |
692 | .main-content .anchor .octicon-link {
693 | width: 16px;
694 | height: 16px;
695 | }
696 |
697 | .main-content .anchor:focus {
698 | outline: none
699 | }
700 |
701 | .main-content h1:hover .anchor,
702 | .main-content h2:hover .anchor,
703 | .main-content h3:hover .anchor,
704 | .main-content h4:hover .anchor,
705 | .main-content h5:hover .anchor,
706 | .main-content h6:hover .anchor {
707 | text-decoration: none
708 | }
709 |
710 | .main-content h1 .octicon-link,
711 | .main-content h2 .octicon-link,
712 | .main-content h3 .octicon-link,
713 | .main-content h4 .octicon-link,
714 | .main-content h5 .octicon-link,
715 | .main-content h6 .octicon-link {
716 | visibility: hidden
717 | }
718 |
719 | .main-content h1:hover .anchor .octicon-link,
720 | .main-content h2:hover .anchor .octicon-link,
721 | .main-content h3:hover .anchor .octicon-link,
722 | .main-content h4:hover .anchor .octicon-link,
723 | .main-content h5:hover .anchor .octicon-link,
724 | .main-content h6:hover .anchor .octicon-link {
725 | visibility: visible
726 | }
727 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | galileo main-dev
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
93 |
94 |
95 |
96 |
<p align="center">
97 | <h1 align="center">Galileo</h1>
98 | <h2 align="center">The fastest way to make the fastest dogehouse bots.</h2>
99 | </p>
100 |
101 |
<h3 align="center">
102 | <a href="https://github.com/willdoescode/galileo/tree/main/examples">Examples</a>
103 | <span> · </span>
104 | <a href="https://docs.will.tools/docs">Documentation</a>
105 | <span> · </span>
106 | <a href="https://github.com/willdoescode/galileo/blob/main/GUIDE.md">Guide</a>
107 | </h3>
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/docs/js/doc.js:
--------------------------------------------------------------------------------
1 | window.CrystalDocs = (window.CrystalDocs || {});
2 |
3 | CrystalDocs.base_path = (CrystalDocs.base_path || "");
4 |
5 | CrystalDocs.searchIndex = (CrystalDocs.searchIndex || false);
6 | CrystalDocs.MAX_RESULTS_DISPLAY = 140;
7 |
8 | CrystalDocs.runQuery = function(query) {
9 | function searchType(type, query, results) {
10 | var matches = [];
11 | var matchedFields = [];
12 | var name = type.full_name;
13 | var i = name.lastIndexOf("::");
14 | if (i > 0) {
15 | name = name.substring(i + 2);
16 | }
17 | var nameMatches = query.matches(name);
18 | if (nameMatches){
19 | matches = matches.concat(nameMatches);
20 | matchedFields.push("name");
21 | }
22 |
23 | var namespaceMatches = query.matchesNamespace(type.full_name);
24 | if(namespaceMatches){
25 | matches = matches.concat(namespaceMatches);
26 | matchedFields.push("name");
27 | }
28 |
29 | var docMatches = query.matches(type.doc);
30 | if(docMatches){
31 | matches = matches.concat(docMatches);
32 | matchedFields.push("doc");
33 | }
34 | if (matches.length > 0) {
35 | results.push({
36 | id: type.id,
37 | result_type: "type",
38 | kind: type.kind,
39 | name: name,
40 | full_name: type.full_name,
41 | href: type.path,
42 | summary: type.summary,
43 | matched_fields: matchedFields,
44 | matched_terms: matches
45 | });
46 | }
47 |
48 | type.instance_methods.forEach(function(method) {
49 | searchMethod(method, type, "instance_method", query, results);
50 | })
51 | type.class_methods.forEach(function(method) {
52 | searchMethod(method, type, "class_method", query, results);
53 | })
54 | type.constructors.forEach(function(constructor) {
55 | searchMethod(constructor, type, "constructor", query, results);
56 | })
57 | type.macros.forEach(function(macro) {
58 | searchMethod(macro, type, "macro", query, results);
59 | })
60 | type.constants.forEach(function(constant){
61 | searchConstant(constant, type, query, results);
62 | });
63 |
64 | type.types.forEach(function(subtype){
65 | searchType(subtype, query, results);
66 | });
67 | };
68 |
69 | function searchMethod(method, type, kind, query, results) {
70 | var matches = [];
71 | var matchedFields = [];
72 | var nameMatches = query.matchesMethod(method.name, kind, type);
73 | if (nameMatches){
74 | matches = matches.concat(nameMatches);
75 | matchedFields.push("name");
76 | }
77 |
78 | method.args.forEach(function(arg){
79 | var argMatches = query.matches(arg.external_name);
80 | if (argMatches) {
81 | matches = matches.concat(argMatches);
82 | matchedFields.push("args");
83 | }
84 | });
85 |
86 | var docMatches = query.matches(type.doc);
87 | if(docMatches){
88 | matches = matches.concat(docMatches);
89 | matchedFields.push("doc");
90 | }
91 |
92 | if (matches.length > 0) {
93 | var typeMatches = query.matches(type.full_name);
94 | if (typeMatches) {
95 | matchedFields.push("type");
96 | matches = matches.concat(typeMatches);
97 | }
98 | results.push({
99 | id: method.id,
100 | type: type.full_name,
101 | result_type: kind,
102 | name: method.name,
103 | full_name: type.full_name + "#" + method.name,
104 | args_string: method.args_string,
105 | summary: method.summary,
106 | href: type.path + "#" + method.id,
107 | matched_fields: matchedFields,
108 | matched_terms: matches
109 | });
110 | }
111 | }
112 |
113 | function searchConstant(constant, type, query, results) {
114 | var matches = [];
115 | var matchedFields = [];
116 | var nameMatches = query.matches(constant.name);
117 | if (nameMatches){
118 | matches = matches.concat(nameMatches);
119 | matchedFields.push("name");
120 | }
121 | var docMatches = query.matches(constant.doc);
122 | if(docMatches){
123 | matches = matches.concat(docMatches);
124 | matchedFields.push("doc");
125 | }
126 | if (matches.length > 0) {
127 | var typeMatches = query.matches(type.full_name);
128 | if (typeMatches) {
129 | matchedFields.push("type");
130 | matches = matches.concat(typeMatches);
131 | }
132 | results.push({
133 | id: constant.id,
134 | type: type.full_name,
135 | result_type: "constant",
136 | name: constant.name,
137 | full_name: type.full_name + "#" + constant.name,
138 | value: constant.value,
139 | summary: constant.summary,
140 | href: type.path + "#" + constant.id,
141 | matched_fields: matchedFields,
142 | matched_terms: matches
143 | });
144 | }
145 | }
146 |
147 | var results = [];
148 | searchType(CrystalDocs.searchIndex.program, query, results);
149 | return results;
150 | };
151 |
152 | CrystalDocs.rankResults = function(results, query) {
153 | function uniqueArray(ar) {
154 | var j = {};
155 |
156 | ar.forEach(function(v) {
157 | j[v + "::" + typeof v] = v;
158 | });
159 |
160 | return Object.keys(j).map(function(v) {
161 | return j[v];
162 | });
163 | }
164 |
165 | results = results.sort(function(a, b) {
166 | var matchedTermsDiff = uniqueArray(b.matched_terms).length - uniqueArray(a.matched_terms).length;
167 | var aHasDocs = b.matched_fields.includes("doc");
168 | var bHasDocs = b.matched_fields.includes("doc");
169 |
170 | var aOnlyDocs = aHasDocs && a.matched_fields.length == 1;
171 | var bOnlyDocs = bHasDocs && b.matched_fields.length == 1;
172 |
173 | if (a.result_type == "type" && b.result_type != "type" && !aOnlyDocs) {
174 | if(CrystalDocs.DEBUG) { console.log("a is type b not"); }
175 | return -1;
176 | } else if (b.result_type == "type" && a.result_type != "type" && !bOnlyDocs) {
177 | if(CrystalDocs.DEBUG) { console.log("b is type, a not"); }
178 | return 1;
179 | }
180 | if (a.matched_fields.includes("name")) {
181 | if (b.matched_fields.includes("name")) {
182 | var a_name = (CrystalDocs.prefixForType(a.result_type) || "") + ((a.result_type == "type") ? a.full_name : a.name);
183 | var b_name = (CrystalDocs.prefixForType(b.result_type) || "") + ((b.result_type == "type") ? b.full_name : b.name);
184 | a_name = a_name.toLowerCase();
185 | b_name = b_name.toLowerCase();
186 | for(var i = 0; i < query.normalizedTerms.length; i++) {
187 | var term = query.terms[i].replace(/^::?|::?$/, "");
188 | var a_orig_index = a_name.indexOf(term);
189 | var b_orig_index = b_name.indexOf(term);
190 | if(CrystalDocs.DEBUG) { console.log("term: " + term + " a: " + a_name + " b: " + b_name); }
191 | if(CrystalDocs.DEBUG) { console.log(a_orig_index, b_orig_index, a_orig_index - b_orig_index); }
192 | if (a_orig_index >= 0) {
193 | if (b_orig_index >= 0) {
194 | if(CrystalDocs.DEBUG) { console.log("both have exact match", a_orig_index > b_orig_index ? -1 : 1); }
195 | if(a_orig_index != b_orig_index) {
196 | if(CrystalDocs.DEBUG) { console.log("both have exact match at different positions", a_orig_index > b_orig_index ? 1 : -1); }
197 | return a_orig_index > b_orig_index ? 1 : -1;
198 | }
199 | } else {
200 | if(CrystalDocs.DEBUG) { console.log("a has exact match, b not"); }
201 | return -1;
202 | }
203 | } else if (b_orig_index >= 0) {
204 | if(CrystalDocs.DEBUG) { console.log("b has exact match, a not"); }
205 | return 1;
206 | }
207 | }
208 | } else {
209 | if(CrystalDocs.DEBUG) { console.log("a has match in name, b not"); }
210 | return -1;
211 | }
212 | } else if (
213 | !a.matched_fields.includes("name") &&
214 | b.matched_fields.includes("name")
215 | ) {
216 | return 1;
217 | }
218 |
219 | if (matchedTermsDiff != 0 || (aHasDocs != bHasDocs)) {
220 | if(CrystalDocs.DEBUG) { console.log("matchedTermsDiff: " + matchedTermsDiff, aHasDocs, bHasDocs); }
221 | return matchedTermsDiff;
222 | }
223 |
224 | var matchedFieldsDiff = b.matched_fields.length - a.matched_fields.length;
225 | if (matchedFieldsDiff != 0) {
226 | if(CrystalDocs.DEBUG) { console.log("matched to different number of fields: " + matchedFieldsDiff); }
227 | return matchedFieldsDiff > 0 ? 1 : -1;
228 | }
229 |
230 | var nameCompare = a.name.localeCompare(b.name);
231 | if(nameCompare != 0){
232 | if(CrystalDocs.DEBUG) { console.log("nameCompare resulted in: " + a.name + "<=>" + b.name + ": " + nameCompare); }
233 | return nameCompare > 0 ? 1 : -1;
234 | }
235 |
236 | if(a.matched_fields.includes("args") && b.matched_fields.includes("args")) {
237 | for(var i = 0; i < query.terms.length; i++) {
238 | var term = query.terms[i];
239 | var aIndex = a.args_string.indexOf(term);
240 | var bIndex = b.args_string.indexOf(term);
241 | if(CrystalDocs.DEBUG) { console.log("index of " + term + " in args_string: " + aIndex + " - " + bIndex); }
242 | if(aIndex >= 0){
243 | if(bIndex >= 0){
244 | if(aIndex != bIndex){
245 | return aIndex > bIndex ? 1 : -1;
246 | }
247 | }else{
248 | return -1;
249 | }
250 | }else if(bIndex >= 0) {
251 | return 1;
252 | }
253 | }
254 | }
255 |
256 | return 0;
257 | });
258 |
259 | if (results.length > 1) {
260 | // if we have more than two search terms, only include results with the most matches
261 | var bestMatchedTerms = uniqueArray(results[0].matched_terms).length;
262 |
263 | results = results.filter(function(result) {
264 | return uniqueArray(result.matched_terms).length + 1 >= bestMatchedTerms;
265 | });
266 | }
267 | return results;
268 | };
269 |
270 | CrystalDocs.prefixForType = function(type) {
271 | switch (type) {
272 | case "instance_method":
273 | return "#";
274 |
275 | case "class_method":
276 | case "macro":
277 | case "constructor":
278 | return ".";
279 |
280 | default:
281 | return false;
282 | }
283 | };
284 |
285 | CrystalDocs.displaySearchResults = function(results, query) {
286 | function sanitize(html){
287 | return html.replace(/<(?!\/?code)[^>]+>/g, "");
288 | }
289 |
290 | // limit results
291 | if (results.length > CrystalDocs.MAX_RESULTS_DISPLAY) {
292 | results = results.slice(0, CrystalDocs.MAX_RESULTS_DISPLAY);
293 | }
294 |
295 | var $frag = document.createDocumentFragment();
296 | var $resultsElem = document.querySelector(".search-list");
297 | $resultsElem.innerHTML = "";
298 |
299 | results.forEach(function(result, i) {
300 | var url = CrystalDocs.base_path + result.href;
301 | var type = false;
302 |
303 | var title = query.highlight(result.result_type == "type" ? result.full_name : result.name);
304 |
305 | var prefix = CrystalDocs.prefixForType(result.result_type);
306 | if (prefix) {
307 | title = "" + prefix + " " + title;
308 | }
309 |
310 | title = "" + title + " ";
311 |
312 | if (result.args_string) {
313 | title +=
314 | "" + query.highlight(result.args_string) + " ";
315 | }
316 |
317 | $elem = document.createElement("li");
318 | $elem.className = "search-result search-result--" + result.result_type;
319 | $elem.dataset.href = url;
320 | $elem.setAttribute("title", result.full_name + " docs page");
321 |
322 | var $title = document.createElement("div");
323 | $title.setAttribute("class", "search-result__title");
324 | var $titleLink = document.createElement("a");
325 | $titleLink.setAttribute("href", url);
326 |
327 | $titleLink.innerHTML = title;
328 | $title.appendChild($titleLink);
329 | $elem.appendChild($title);
330 | $elem.addEventListener("click", function() {
331 | $titleLink.click();
332 | });
333 |
334 | if (result.result_type !== "type") {
335 | var $type = document.createElement("div");
336 | $type.setAttribute("class", "search-result__type");
337 | $type.innerHTML = query.highlight(result.type);
338 | $elem.appendChild($type);
339 | }
340 |
341 | if(result.summary){
342 | var $doc = document.createElement("div");
343 | $doc.setAttribute("class", "search-result__doc");
344 | $doc.innerHTML = query.highlight(sanitize(result.summary));
345 | $elem.appendChild($doc);
346 | }
347 |
348 | $elem.appendChild(document.createComment(JSON.stringify(result)));
349 | $frag.appendChild($elem);
350 | });
351 |
352 | $resultsElem.appendChild($frag);
353 |
354 | CrystalDocs.toggleResultsList(true);
355 | };
356 |
357 | CrystalDocs.toggleResultsList = function(visible) {
358 | if (visible) {
359 | document.querySelector(".types-list").classList.add("hidden");
360 | document.querySelector(".search-results").classList.remove("hidden");
361 | } else {
362 | document.querySelector(".types-list").classList.remove("hidden");
363 | document.querySelector(".search-results").classList.add("hidden");
364 | }
365 | };
366 |
367 | CrystalDocs.Query = function(string) {
368 | this.original = string;
369 | this.terms = string.split(/\s+/).filter(function(word) {
370 | return CrystalDocs.Query.stripModifiers(word).length > 0;
371 | });
372 |
373 | var normalized = this.terms.map(CrystalDocs.Query.normalizeTerm);
374 | this.normalizedTerms = normalized;
375 |
376 | function runMatcher(field, matcher) {
377 | if (!field) {
378 | return false;
379 | }
380 | var normalizedValue = CrystalDocs.Query.normalizeTerm(field);
381 |
382 | var matches = [];
383 | normalized.forEach(function(term) {
384 | if (matcher(normalizedValue, term)) {
385 | matches.push(term);
386 | }
387 | });
388 | return matches.length > 0 ? matches : false;
389 | }
390 |
391 | this.matches = function(field) {
392 | return runMatcher(field, function(normalized, term) {
393 | if (term[0] == "#" || term[0] == ".") {
394 | return false;
395 | }
396 | return normalized.indexOf(term) >= 0;
397 | });
398 | };
399 |
400 | function namespaceMatcher(normalized, term){
401 | var i = term.indexOf(":");
402 | if(i >= 0){
403 | term = term.replace(/^::?|::?$/, "");
404 | var index = normalized.indexOf(term);
405 | if((index == 0) || (index > 0 && normalized[index-1] == ":")){
406 | return true;
407 | }
408 | }
409 | return false;
410 | }
411 | this.matchesMethod = function(name, kind, type) {
412 | return runMatcher(name, function(normalized, term) {
413 | var i = term.indexOf("#");
414 | if(i >= 0){
415 | if (kind != "instance_method") {
416 | return false;
417 | }
418 | }else{
419 | i = term.indexOf(".");
420 | if(i >= 0){
421 | if (kind != "class_method" && kind != "macro" && kind != "constructor") {
422 | return false;
423 | }
424 | }else{
425 | //neither # nor .
426 | if(term.indexOf(":") && namespaceMatcher(normalized, term)){
427 | return true;
428 | }
429 | }
430 | }
431 |
432 | var methodName = term;
433 | if(i >= 0){
434 | var termType = term.substring(0, i);
435 | methodName = term.substring(i+1);
436 |
437 | if(termType != "") {
438 | if(CrystalDocs.Query.normalizeTerm(type.full_name).indexOf(termType) < 0){
439 | return false;
440 | }
441 | }
442 | }
443 | return normalized.indexOf(methodName) >= 0;
444 | });
445 | };
446 |
447 | this.matchesNamespace = function(namespace){
448 | return runMatcher(namespace, namespaceMatcher);
449 | };
450 |
451 | this.highlight = function(string) {
452 | if (typeof string == "undefined") {
453 | return "";
454 | }
455 | function escapeRegExp(s) {
456 | return s.replace(/[.*+?\^${}()|\[\]\\]/g, "\\$&").replace(/^[#\.:]+/, "");
457 | }
458 | return string.replace(
459 | new RegExp("(" + this.normalizedTerms.map(escapeRegExp).join("|") + ")", "gi"),
460 | "$1 "
461 | );
462 | };
463 | };
464 | CrystalDocs.Query.normalizeTerm = function(term) {
465 | return term.toLowerCase();
466 | };
467 | CrystalDocs.Query.stripModifiers = function(term) {
468 | switch (term[0]) {
469 | case "#":
470 | case ".":
471 | case ":":
472 | return term.substr(1);
473 |
474 | default:
475 | return term;
476 | }
477 | }
478 |
479 | CrystalDocs.search = function(string) {
480 | if(!CrystalDocs.searchIndex) {
481 | console.log("CrystalDocs search index not initialized, delaying search");
482 |
483 | document.addEventListener("CrystalDocs:loaded", function listener(){
484 | document.removeEventListener("CrystalDocs:loaded", listener);
485 | CrystalDocs.search(string);
486 | });
487 | return;
488 | }
489 |
490 | document.dispatchEvent(new Event("CrystalDocs:searchStarted"));
491 |
492 | var query = new CrystalDocs.Query(string);
493 | var results = CrystalDocs.runQuery(query);
494 | results = CrystalDocs.rankResults(results, query);
495 | CrystalDocs.displaySearchResults(results, query);
496 |
497 | document.dispatchEvent(new Event("CrystalDocs:searchPerformed"));
498 | };
499 |
500 | CrystalDocs.initializeIndex = function(data) {
501 | CrystalDocs.searchIndex = data;
502 |
503 | document.dispatchEvent(new Event("CrystalDocs:loaded"));
504 | };
505 |
506 | CrystalDocs.loadIndex = function() {
507 | function loadJSON(file, callback) {
508 | var xobj = new XMLHttpRequest();
509 | xobj.overrideMimeType("application/json");
510 | xobj.open("GET", file, true);
511 | xobj.onreadystatechange = function() {
512 | if (xobj.readyState == 4 && xobj.status == "200") {
513 | callback(xobj.responseText);
514 | }
515 | };
516 | xobj.send(null);
517 | }
518 |
519 | function loadScript(file) {
520 | script = document.createElement("script");
521 | script.src = file;
522 | document.body.appendChild(script);
523 | }
524 |
525 | function parseJSON(json) {
526 | CrystalDocs.initializeIndex(JSON.parse(json));
527 | }
528 |
529 | for(var i = 0; i < document.scripts.length; i++){
530 | var script = document.scripts[i];
531 | if (script.src && script.src.indexOf("js/doc.js") >= 0) {
532 | if (script.src.indexOf("file://") == 0) {
533 | // We need to support JSONP files for the search to work on local file system.
534 | var jsonPath = script.src.replace("js/doc.js", "search-index.js");
535 | loadScript(jsonPath);
536 | return;
537 | } else {
538 | var jsonPath = script.src.replace("js/doc.js", "index.json");
539 | loadJSON(jsonPath, parseJSON);
540 | return;
541 | }
542 | }
543 | }
544 | console.error("Could not find location of js/doc.js");
545 | };
546 |
547 | // Callback for jsonp
548 | function crystal_doc_search_index_callback(data) {
549 | CrystalDocs.initializeIndex(data);
550 | }
551 |
552 | Navigator = function(sidebar, searchInput, list, leaveSearchScope){
553 | this.list = list;
554 | var self = this;
555 |
556 | var performingSearch = false;
557 |
558 | document.addEventListener('CrystalDocs:searchStarted', function(){
559 | performingSearch = true;
560 | });
561 | document.addEventListener('CrystalDocs:searchDebounceStarted', function(){
562 | performingSearch = true;
563 | });
564 | document.addEventListener('CrystalDocs:searchPerformed', function(){
565 | performingSearch = false;
566 | });
567 | document.addEventListener('CrystalDocs:searchDebounceStopped', function(event){
568 | performingSearch = false;
569 | });
570 |
571 | function delayWhileSearching(callback) {
572 | if(performingSearch){
573 | document.addEventListener('CrystalDocs:searchPerformed', function listener(){
574 | document.removeEventListener('CrystalDocs:searchPerformed', listener);
575 |
576 | // add some delay to let search results display kick in
577 | setTimeout(callback, 100);
578 | });
579 | }else{
580 | callback();
581 | }
582 | }
583 |
584 | function clearMoveTimeout() {
585 | clearTimeout(self.moveTimeout);
586 | self.moveTimeout = null;
587 | }
588 |
589 | function startMoveTimeout(upwards){
590 | /*if(self.moveTimeout) {
591 | clearMoveTimeout();
592 | }
593 |
594 | var go = function() {
595 | if (!self.moveTimeout) return;
596 | self.move(upwards);
597 | self.moveTimout = setTimeout(go, 600);
598 | };
599 | self.moveTimeout = setTimeout(go, 800);*/
600 | }
601 |
602 | function scrollCenter(element) {
603 | var rect = element.getBoundingClientRect();
604 | var middle = sidebar.clientHeight / 2;
605 | sidebar.scrollTop += rect.top + rect.height / 2 - middle;
606 | }
607 |
608 | var move = this.move = function(upwards){
609 | if(!this.current){
610 | this.highlightFirst();
611 | return true;
612 | }
613 | var next = upwards ? this.current.previousElementSibling : this.current.nextElementSibling;
614 | if(next && next.classList) {
615 | this.highlight(next);
616 | scrollCenter(next);
617 | return true;
618 | }
619 | return false;
620 | };
621 |
622 | this.moveRight = function(){
623 | };
624 | this.moveLeft = function(){
625 | };
626 |
627 | this.highlight = function(elem) {
628 | if(!elem){
629 | return;
630 | }
631 | this.removeHighlight();
632 |
633 | this.current = elem;
634 | this.current.classList.add("current");
635 | };
636 |
637 | this.highlightFirst = function(){
638 | this.highlight(this.list.querySelector('li:first-child'));
639 | };
640 |
641 | this.removeHighlight = function() {
642 | if(this.current){
643 | this.current.classList.remove("current");
644 | }
645 | this.current = null;
646 | }
647 |
648 | this.openSelectedResult = function() {
649 | if(this.current) {
650 | this.current.click();
651 | }
652 | }
653 |
654 | this.focus = function() {
655 | searchInput.focus();
656 | searchInput.select();
657 | this.highlightFirst();
658 | }
659 |
660 | function handleKeyUp(event) {
661 | switch(event.key) {
662 | case "ArrowUp":
663 | case "ArrowDown":
664 | case "i":
665 | case "j":
666 | case "k":
667 | case "l":
668 | case "c":
669 | case "h":
670 | case "t":
671 | case "n":
672 | event.stopPropagation();
673 | clearMoveTimeout();
674 | }
675 | }
676 |
677 | function handleKeyDown(event) {
678 | switch(event.key) {
679 | case "Enter":
680 | event.stopPropagation();
681 | event.preventDefault();
682 | leaveSearchScope();
683 | self.openSelectedResult();
684 | break;
685 | case "Escape":
686 | event.stopPropagation();
687 | event.preventDefault();
688 | leaveSearchScope();
689 | break;
690 | case "j":
691 | case "c":
692 | case "ArrowUp":
693 | if(event.ctrlKey || event.key == "ArrowUp") {
694 | event.stopPropagation();
695 | self.move(true);
696 | startMoveTimeout(true);
697 | }
698 | break;
699 | case "k":
700 | case "h":
701 | case "ArrowDown":
702 | if(event.ctrlKey || event.key == "ArrowDown") {
703 | event.stopPropagation();
704 | self.move(false);
705 | startMoveTimeout(false);
706 | }
707 | break;
708 | case "k":
709 | case "t":
710 | case "ArrowLeft":
711 | if(event.ctrlKey || event.key == "ArrowLeft") {
712 | event.stopPropagation();
713 | self.moveLeft();
714 | }
715 | break;
716 | case "l":
717 | case "n":
718 | case "ArrowRight":
719 | if(event.ctrlKey || event.key == "ArrowRight") {
720 | event.stopPropagation();
721 | self.moveRight();
722 | }
723 | break;
724 | }
725 | }
726 |
727 | function handleInputKeyUp(event) {
728 | switch(event.key) {
729 | case "ArrowUp":
730 | case "ArrowDown":
731 | event.stopPropagation();
732 | event.preventDefault();
733 | clearMoveTimeout();
734 | }
735 | }
736 |
737 | function handleInputKeyDown(event) {
738 | switch(event.key) {
739 | case "Enter":
740 | event.stopPropagation();
741 | event.preventDefault();
742 | delayWhileSearching(function(){
743 | self.openSelectedResult();
744 | leaveSearchScope();
745 | });
746 | break;
747 | case "Escape":
748 | event.stopPropagation();
749 | event.preventDefault();
750 | // remove focus from search input
751 | leaveSearchScope();
752 | sidebar.focus();
753 | break;
754 | case "ArrowUp":
755 | event.stopPropagation();
756 | event.preventDefault();
757 | self.move(true);
758 | startMoveTimeout(true);
759 | break;
760 |
761 | case "ArrowDown":
762 | event.stopPropagation();
763 | event.preventDefault();
764 | self.move(false);
765 | startMoveTimeout(false);
766 | break;
767 | }
768 | }
769 |
770 | sidebar.tabIndex = 100; // set tabIndex to enable keylistener
771 | sidebar.addEventListener('keyup', function(event) {
772 | handleKeyUp(event);
773 | });
774 | sidebar.addEventListener('keydown', function(event) {
775 | handleKeyDown(event);
776 | });
777 | searchInput.addEventListener('keydown', function(event) {
778 | handleInputKeyDown(event);
779 | });
780 | searchInput.addEventListener('keyup', function(event) {
781 | handleInputKeyUp(event);
782 | });
783 | this.move();
784 | };
785 |
786 | CrystalDocs.initializeVersions = function () {
787 | function loadJSON(file, callback) {
788 | var xobj = new XMLHttpRequest();
789 | xobj.overrideMimeType("application/json");
790 | xobj.open("GET", file, true);
791 | xobj.onreadystatechange = function() {
792 | if (xobj.readyState == 4 && xobj.status == "200") {
793 | callback(xobj.responseText);
794 | }
795 | };
796 | xobj.send(null);
797 | }
798 |
799 | function parseJSON(json) {
800 | CrystalDocs.loadConfig(JSON.parse(json));
801 | }
802 |
803 | $elem = document.querySelector("html > head > meta[name=\"crystal_docs.json_config_url\"]")
804 | if ($elem == undefined) {
805 | return
806 | }
807 | jsonURL = $elem.getAttribute("content")
808 | if (jsonURL && jsonURL != "") {
809 | loadJSON(jsonURL, parseJSON);
810 | }
811 | }
812 |
813 | CrystalDocs.loadConfig = function (config) {
814 | var projectVersions = config["versions"]
815 | var currentVersion = document.querySelector("html > head > meta[name=\"crystal_docs.project_version\"]").getAttribute("content")
816 |
817 | var currentVersionInList = projectVersions.find(function (element) {
818 | return element.name == currentVersion
819 | })
820 |
821 | if (!currentVersionInList) {
822 | projectVersions.unshift({ name: currentVersion, url: '#' })
823 | }
824 |
825 | $version = document.querySelector(".project-summary > .project-version")
826 | $version.innerHTML = ""
827 |
828 | $select = document.createElement("select")
829 | $select.classList.add("project-versions-nav")
830 | $select.addEventListener("change", function () {
831 | window.location.href = this.value
832 | })
833 | projectVersions.forEach(function (version) {
834 | $item = document.createElement("option")
835 | $item.setAttribute("value", version.url)
836 | $item.append(document.createTextNode(version.name))
837 |
838 | if (version.name == currentVersion) {
839 | $item.setAttribute("selected", true)
840 | $item.setAttribute("disabled", true)
841 | }
842 | $select.append($item)
843 | });
844 | $form = document.createElement("form")
845 | $form.setAttribute("autocomplete", "off")
846 | $form.append($select)
847 | $version.append($form)
848 | }
849 |
850 | document.addEventListener("DOMContentLoaded", function () {
851 | CrystalDocs.initializeVersions()
852 | })
853 |
854 | var UsageModal = function(title, content) {
855 | var $body = document.body;
856 | var self = this;
857 | var $modalBackground = document.createElement("div");
858 | $modalBackground.classList.add("modal-background");
859 | var $usageModal = document.createElement("div");
860 | $usageModal.classList.add("usage-modal");
861 | $modalBackground.appendChild($usageModal);
862 | var $title = document.createElement("h3");
863 | $title.classList.add("modal-title");
864 | $title.innerHTML = title
865 | $usageModal.appendChild($title);
866 | var $closeButton = document.createElement("span");
867 | $closeButton.classList.add("close-button");
868 | $closeButton.setAttribute("title", "Close modal");
869 | $closeButton.innerText = '×';
870 | $usageModal.appendChild($closeButton);
871 | $usageModal.insertAdjacentHTML("beforeend", content);
872 |
873 | $modalBackground.addEventListener('click', function(event) {
874 | var element = event.target || event.srcElement;
875 |
876 | if(element == $modalBackground) {
877 | self.hide();
878 | }
879 | });
880 | $closeButton.addEventListener('click', function(event) {
881 | self.hide();
882 | });
883 |
884 | $body.insertAdjacentElement('beforeend', $modalBackground);
885 |
886 | this.show = function(){
887 | $body.classList.add("js-modal-visible");
888 | };
889 | this.hide = function(){
890 | $body.classList.remove("js-modal-visible");
891 | };
892 | this.isVisible = function(){
893 | return $body.classList.contains("js-modal-visible");
894 | }
895 | }
896 |
897 |
898 | document.addEventListener('DOMContentLoaded', function() {
899 | var sessionStorage;
900 | try {
901 | sessionStorage = window.sessionStorage;
902 | } catch (e) { }
903 | if(!sessionStorage) {
904 | sessionStorage = {
905 | setItem: function() {},
906 | getItem: function() {},
907 | removeItem: function() {}
908 | };
909 | }
910 |
911 | var repositoryName = document.querySelector('[name=repository-name]').getAttribute('content');
912 | var typesList = document.querySelector('.types-list');
913 | var searchInput = document.querySelector('.search-input');
914 | var parents = document.querySelectorAll('.types-list li.parent');
915 |
916 | var scrollSidebarToOpenType = function(){
917 | var openTypes = typesList.querySelectorAll('.current');
918 | if (openTypes.length > 0) {
919 | var lastOpenType = openTypes[openTypes.length - 1];
920 | lastOpenType.scrollIntoView();
921 | }
922 | }
923 |
924 | scrollSidebarToOpenType();
925 |
926 | var setPersistentSearchQuery = function(value){
927 | sessionStorage.setItem(repositoryName + '::search-input:value', value);
928 | }
929 |
930 | for(var i = 0; i < parents.length; i++) {
931 | var _parent = parents[i];
932 | _parent.addEventListener('click', function(e) {
933 | e.stopPropagation();
934 |
935 | if(e.target.tagName.toLowerCase() == 'li') {
936 | if(e.target.className.match(/open/)) {
937 | sessionStorage.removeItem(e.target.getAttribute('data-id'));
938 | e.target.className = e.target.className.replace(/ +open/g, '');
939 | } else {
940 | sessionStorage.setItem(e.target.getAttribute('data-id'), '1');
941 | if(e.target.className.indexOf('open') == -1) {
942 | e.target.className += ' open';
943 | }
944 | }
945 | }
946 | });
947 |
948 | if(sessionStorage.getItem(_parent.getAttribute('data-id')) == '1') {
949 | _parent.className += ' open';
950 | }
951 | }
952 |
953 | var leaveSearchScope = function(){
954 | CrystalDocs.toggleResultsList(false);
955 | window.focus();
956 | }
957 |
958 | var navigator = new Navigator(document.querySelector('.types-list'), searchInput, document.querySelector(".search-results"), leaveSearchScope);
959 |
960 | CrystalDocs.loadIndex();
961 | var searchTimeout;
962 | var lastSearchText = false;
963 | var performSearch = function() {
964 | document.dispatchEvent(new Event("CrystalDocs:searchDebounceStarted"));
965 |
966 | clearTimeout(searchTimeout);
967 | searchTimeout = setTimeout(function() {
968 | var text = searchInput.value;
969 |
970 | if(text == "") {
971 | CrystalDocs.toggleResultsList(false);
972 | }else if(text == lastSearchText){
973 | document.dispatchEvent(new Event("CrystalDocs:searchDebounceStopped"));
974 | }else{
975 | CrystalDocs.search(text);
976 | navigator.highlightFirst();
977 | searchInput.focus();
978 | }
979 | lastSearchText = text;
980 | setPersistentSearchQuery(text);
981 | }, 200);
982 | };
983 |
984 | if(location.hash.length > 3 && location.hash.substring(0,3) == "#q="){
985 | // allows directly linking a search query which is then executed on the client
986 | // this comes handy for establishing a custom browser search engine with https://crystal-lang.org/api/#q=%s as a search URL
987 | // TODO: Add OpenSearch description
988 | var searchQuery = location.hash.substring(3);
989 | history.pushState({searchQuery: searchQuery}, "Search for " + searchQuery, location.href.replace(/#q=.*/, ""));
990 | searchInput.value = searchQuery;
991 | document.addEventListener('CrystalDocs:loaded', performSearch);
992 | }
993 |
994 | if (searchInput.value.length == 0) {
995 | var searchText = sessionStorage.getItem(repositoryName + '::search-input:value');
996 | if(searchText){
997 | searchInput.value = searchText;
998 | }
999 | }
1000 | searchInput.addEventListener('keyup', performSearch);
1001 | searchInput.addEventListener('input', performSearch);
1002 |
1003 | var usageModal = new UsageModal('Keyboard Shortcuts', '' +
1004 | '' +
1005 | ' ' +
1006 | ' ' +
1007 | ' s ,' +
1008 | ' / ' +
1009 | ' ' +
1010 | ' Search' +
1011 | ' ' +
1012 | ' ' +
1013 | ' Esc ' +
1014 | ' Abort search / Close modal' +
1015 | ' ' +
1016 | ' ' +
1017 | ' ' +
1018 | ' ⇨ ,' +
1019 | ' Enter ' +
1020 | ' ' +
1021 | ' Open highlighted result' +
1022 | ' ' +
1023 | ' ' +
1024 | ' ' +
1025 | ' ⇧ ,' +
1026 | ' Ctrl+j ' +
1027 | ' ' +
1028 | ' Select previous result' +
1029 | ' ' +
1030 | ' ' +
1031 | ' ' +
1032 | ' ⇩ ,' +
1033 | ' Ctrl+k ' +
1034 | ' ' +
1035 | ' Select next result' +
1036 | ' ' +
1037 | ' ' +
1038 | ' ? ' +
1039 | ' Show usage info' +
1040 | ' ' +
1041 | ' '
1042 | );
1043 |
1044 | function handleShortkeys(event) {
1045 | var element = event.target || event.srcElement;
1046 |
1047 | if(element.tagName == "INPUT" || element.tagName == "TEXTAREA" || element.parentElement.tagName == "TEXTAREA"){
1048 | return;
1049 | }
1050 |
1051 | switch(event.key) {
1052 | case "?":
1053 | usageModal.show();
1054 | break;
1055 |
1056 | case "Escape":
1057 | usageModal.hide();
1058 | break;
1059 |
1060 | case "s":
1061 | case "/":
1062 | if(usageModal.isVisible()) {
1063 | return;
1064 | }
1065 | event.stopPropagation();
1066 | navigator.focus();
1067 | performSearch();
1068 | break;
1069 | }
1070 | }
1071 |
1072 | document.addEventListener('keyup', handleShortkeys);
1073 |
1074 | var scrollToEntryFromLocationHash = function() {
1075 | var hash = window.location.hash;
1076 | if (hash) {
1077 | var targetAnchor = decodeURI(hash.substr(1));
1078 | var targetEl = document.getElementById(targetAnchor)
1079 | if (targetEl) {
1080 | targetEl.offsetParent.scrollTop = targetEl.offsetTop;
1081 | }
1082 | }
1083 | };
1084 | window.addEventListener("hashchange", scrollToEntryFromLocationHash, false);
1085 | scrollToEntryFromLocationHash();
1086 | });
1087 |
--------------------------------------------------------------------------------
/docs/toplevel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Top Level Namespace - galileo main-dev
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
93 |
94 |
95 |
96 |
97 |
98 | Top Level Namespace
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
118 |
119 |
120 | Spec::Expectations
121 |
122 | Spec::Methods
123 |
124 |
125 |
126 |
127 |
128 |
136 |
137 |
138 | Spec::Expectations
139 |
140 | Spec::Methods
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
159 |
160 |
161 |
162 |
163 |
164 |
172 |
173 |
174 |
175 |
176 | ACTIVE_SPEAKER_CHANGE = "active_speaker_change"
177 |
178 |
179 |
180 |
181 | ADD_SPEAKER = "add_speaker"
182 |
183 |
184 |
185 |
186 | API_URL = "wss://api.dogehouse.tv/socket"
187 |
188 |
189 |
190 |
191 | ASK_TO_SPEAK = "ask_to_speak"
192 |
193 |
194 |
195 |
196 | AUTH_GOOD = "auth-good"
197 |
198 |
199 |
200 |
201 | BOT_IS_NOW_SPEAKER = "you-are-now-a-speaker"
202 |
203 |
204 |
205 |
206 | BOT_JOINED_AS_PEER = "you-joined-as-peer"
207 |
208 |
209 |
210 |
211 | BOT_JOINED_AS_SPEAKER = "you-joined-as-speaker"
212 |
213 |
214 |
215 |
216 | BOT_LEFT_ROOM = "you_left_room"
217 |
218 |
219 |
220 |
221 | CHANGE_MOD_STATUS = "change_mod_status"
222 |
223 |
224 |
225 |
226 | CHAT_USER_BANNED = "chat_user_banned"
227 |
228 |
229 |
230 |
231 | CREATE_ROOM = "room:create"
232 |
233 |
234 |
235 |
236 | DELETE_ROOM_CHAT_MESSAGE = "delete_room_chat_message"
237 |
238 |
239 |
240 |
241 | ERROR = "error"
242 |
243 |
244 |
245 |
246 | FETCH_DONE = "fetch_done"
247 |
248 |
249 |
250 |
251 | FOLLOW = "follow"
252 |
253 |
254 |
255 |
256 | GET_CURRENT_ROOM_USERS = "get_current_room_users"
257 |
258 |
259 |
260 |
261 | GET_CURRENT_ROOM_USERS_DONE = "get_current_room_users_done"
262 |
263 |
264 |
265 |
266 | GET_FOLLOW_LIST = "get_follow_list"
267 |
268 |
269 |
270 |
271 | GET_TOP_PUBLIC_ROOMS = "get_top_public_rooms"
272 |
273 |
274 |
275 |
276 | GET_TOP_PUBLIC_ROOMS_DONE = "get_top_public_rooms_done"
277 |
278 |
279 |
280 |
281 | GET_USER_PROFILE = "get_user_profile"
282 |
283 |
284 |
285 |
286 | HAND_RAISED = "hand_raised"
287 |
288 |
289 |
290 |
291 | JOIN_ROOM = "join_room"
292 |
293 |
294 |
295 |
296 | JOIN_ROOM_DONE = "join_room_done"
297 |
298 |
299 |
300 |
301 | MOD_CHANGED = "mod_changed"
302 |
303 |
304 |
305 |
306 | MUTE = "mute"
307 |
308 |
309 |
310 |
311 | NEW_CHAT_MESSAGE = "new_chat_msg"
312 |
313 |
314 |
315 |
316 | NEW_PEER_SPEAKER = "new-peer-speaker"
317 |
318 |
319 |
320 |
321 | NEW_TOKENS = "new-tokens"
322 |
323 |
324 |
325 |
326 | PING_TIMEOUT = 8
327 |
328 |
329 |
330 |
331 | SEND_ROOM_CHAT_MSG = "send_room_chat_msg"
332 |
333 |
334 |
335 |
336 | SET_LISTENER = "set_listener"
337 |
338 |
339 |
340 |
341 | SET_ROLE = "room:set_role"
342 |
343 |
344 |
345 |
346 | SET_SPEAKER = "room:set_active_speaker"
347 |
348 |
349 |
350 |
351 | SPEAKER_REMOVED = "speaker_removed"
352 |
353 |
354 |
355 |
356 | TOP_ROOMS = "room:get_top"
357 |
358 |
359 |
360 |
361 | USER_JOINED_ROOM = "new_user_join_room"
362 |
363 |
364 |
365 |
366 | USER_LEFT_ROOM = "user_left_room"
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
--------------------------------------------------------------------------------
/examples/on_message.cr:
--------------------------------------------------------------------------------
1 | require "galileo"
2 |
3 | client = Galileo.new ENV["ACCESS_TOKEN"], ENV["REFRESH_TOKEN"]
4 | client.join_room ENV["ROOM_ID"]
5 |
6 | client.on_message do |msg|
7 | if msg.content.starts_with? "/echo "
8 | client.send msg.content[5..]
9 | end
10 | puts "MSG: #{msg}"
11 | end
12 |
13 | client.run
14 |
--------------------------------------------------------------------------------
/examples/on_ready.cr:
--------------------------------------------------------------------------------
1 | require "galileo"
2 |
3 | client = Galileo.new ENV["ACCESS_TOKEN"], ENV["REFRESH_TOKEN"]
4 | client.join_room ENV["ROOM_ID"]
5 |
6 | client.on_ready do |bot|
7 | puts bot.display_name
8 | puts bot.username
9 | end
10 |
11 | client.run
12 |
--------------------------------------------------------------------------------
/examples/on_room_join.cr:
--------------------------------------------------------------------------------
1 | require "galileo"
2 |
3 | client = Galileo.new ENV["ACCESS_TOKEN"], ENV["REFRESH_TOKEN"]
4 | client.join_room ENV["ROOM_ID"]
5 |
6 | client.on_room_join do |room|
7 | p! "Joined room: #{room}"
8 | end
9 |
10 | client.run
11 |
--------------------------------------------------------------------------------
/examples/on_user_joined_room.cr:
--------------------------------------------------------------------------------
1 | require "galileo"
2 |
3 | client = Galileo.new ENV["ACCESS_TOKEN"], ENV["REFRESH_TOKEN"]
4 | client.join_room ENV["ROOM_ID"]
5 |
6 | client.on_user_joined_room do |user|
7 | client.send "#{user.display_name} joined the room"
8 | end
9 |
10 | client.run
11 |
--------------------------------------------------------------------------------
/shard.yml:
--------------------------------------------------------------------------------
1 | name: galileo
2 | version: 0.1.0
3 |
4 | authors:
5 | - Will Lane
6 |
7 | crystal: 1.0.0
8 |
9 | license: MIT
10 |
--------------------------------------------------------------------------------
/spec/galileo_spec.cr:
--------------------------------------------------------------------------------
1 | require "./spec_helper"
2 |
3 | describe Galileo do
4 | it "auths" do
5 | client = Galileo.new ENV["ACCESS_TOKEN"], ENV["REFRESH_TOKEN"]
6 | client.join_room ENV["ROOM_ID"]
7 |
8 | client.on_ready do |bot|
9 | end
10 |
11 | client.on_message do |msg|
12 | puts "ROOMS: #{client.rooms}"
13 | if msg.content == "ASK TO SPEAK"
14 | client.ask_to_speak
15 | end
16 |
17 | if msg.content == "MUTE"
18 | client.toggle_mute
19 | end
20 |
21 | if msg.content == "SPEAK"
22 | client.set_speaking !client.speaking
23 | end
24 |
25 | if msg.content.starts_with? "/echo "
26 | client.send msg.content[5..]
27 | end
28 | p! "MSG: #{msg}"
29 | end
30 |
31 | client.on_new_tokens do |token, refresh_token|
32 | puts "Received new tokens"
33 | end
34 |
35 | client.on_user_joined_room do |user|
36 | client.send "@#{user.username} joined the room"
37 | end
38 |
39 | # client.on_ping do |context|
40 | # puts "ping"
41 | # end
42 |
43 | client.on_room_join do |room|
44 | puts "Joined room: #{room.name}"
45 | client.ask_to_speak
46 | client.send "normal text test"
47 | # spawn do
48 | # loop do
49 | # client.set_role "raised_hand", "c8799756-7438-45b1-96fe-20f6d3a28b75"
50 | # sleep 1
51 | # end
52 | # end
53 | end
54 |
55 | client.on_all do |msg|
56 | puts msg
57 | end
58 |
59 | # spawn do
60 | # loop do
61 | # client.toggle_mute
62 | # end
63 | # end
64 | client.run
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/spec/spec_helper.cr:
--------------------------------------------------------------------------------
1 | require "spec"
2 | require "../src/galileo"
3 |
--------------------------------------------------------------------------------
/src/galileo.cr:
--------------------------------------------------------------------------------
1 | require "./galileo/*"
2 |
3 | module Galileo
4 | # Create a new client with yoru token and refreshToken
5 | # ```
6 | # client = DogehouseCr.new "token", "refreshToken"
7 | # ```
8 | def self.new(token : String, refreshToken : String)
9 | Client.new token, refreshToken
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/src/galileo/client.cr:
--------------------------------------------------------------------------------
1 | require "http/web_socket"
2 | require "spec"
3 | require "json"
4 | require "./message.cr"
5 | require "./user.cr"
6 | require "./room.cr"
7 | require "./ops.cr"
8 |
9 | API_URL = "wss://api.dogehouse.tv/socket"
10 | PING_TIMEOUT = 8
11 |
12 | # Base dogehouse cilent to interface with api with
13 | class Galileo::Client
14 | # Message queue to send withing message delays
15 | @message_queue = Array(String).new
16 |
17 | @myself : User? = nil
18 |
19 | # The chat delay withing rooms (measured in nanoseconds)
20 | property delay = 1000000
21 |
22 | # True if bot is muted, False if not
23 | property muted : Bool = true
24 |
25 | # True if bot in room, False if not
26 | property in_room : Bool = false
27 |
28 | # Returns all the available rooms in array
29 | # Updated every 2 seconds
30 | getter rooms : Array(Room) = Array(Room).new
31 |
32 | # If bot is speaking will be true else will be false
33 | getter speaking : Bool = false
34 |
35 | # Websocket connection state
36 | property ws : HTTP::WebSocket
37 |
38 | # Optional on ready callback property
39 | # ```
40 | # client.on_ready do |user|
41 | # puts user.display_name
42 | # end
43 | # ```
44 | property ready_callback : (User -> Nil)?
45 |
46 | # Optional message_callback property
47 | # Add a message callback function with .on_message
48 | # ```
49 | # client.on_message do |msg|
50 | # puts msg
51 | # end
52 | # ```
53 | property message_callback : (Message -> Nil)?
54 |
55 | # All messages will be sent through this callback.
56 | # ```
57 | # client.on_all do |msg|
58 | # puts msg
59 | # end
60 | # ```
61 | property all_callback : (String -> Nil)?
62 |
63 | # When room is joined this callback will be called
64 | # ```
65 | # client.on_room_join do |room|
66 | # puts room.name
67 | # end
68 | # ```
69 | property join_room_callback : (Room -> Nil)?
70 |
71 | # If newtokens are passed this callback will be called
72 | # ```
73 | # client.on_new_tokens do |token, refresh_token|
74 | # puts token
75 | # end
76 | # ```
77 | property new_tokens_callback : (String, String -> Nil)?
78 |
79 | # If a user joins a room this callback will be called
80 | # ```
81 | # client.on_user_joined_room do |user|
82 | # puts user.display_name
83 | # end
84 | # ```
85 | property user_joined_room_callback : (User -> Nil)?
86 |
87 | # Client takes your dogehouse token and refreshToken in order to auth
88 | def initialize(@token : String, @refresh_token : String)
89 | @ws = HTTP::WebSocket.new(URI.parse(API_URL))
90 |
91 | auth
92 | end
93 |
94 | def auth
95 | @ws.send(
96 | {
97 | "op" => "auth",
98 | "d" => {
99 | "accessToken" => @token,
100 | "refreshToken" => @refresh_token,
101 | },
102 | "reconnectToVoice" => false,
103 | "muted" => true,
104 | "platform" => "dogehouse_cr",
105 | }.to_json
106 | )
107 | end
108 |
109 | # Takes a role "raised_hand", "listener", "speaker" and an optional userId which will default to the bot userId
110 | def set_role(role : String, userId : String?)
111 | @ws.send(
112 | {
113 | "op" => SET_ROLE,
114 | "p" => {
115 | "role" => role,
116 | "userId" => userId.nil? ? @myself.not_nil!.id : userId.not_nil!,
117 | },
118 | "v" => "0.2.0",
119 | "ref" => "[uuid]",
120 | }.to_json
121 | )
122 | end
123 |
124 | # Send raw messages api without wrapper
125 | def raw_send(msg)
126 | @ws.send msg
127 | end
128 |
129 | # Add a message callback
130 | # on_message takes block with context parameter and String parameter
131 | # ```
132 | # client.on_message do |msg|
133 | # puts msg
134 | # end
135 | # ```
136 | def on_message(&block : Message -> Nil)
137 | @message_callback = block
138 | end
139 |
140 | # Add all callback
141 | # All messages recieved in the client will be sent through this callback
142 | # ```
143 | # client.on_all do |msg|
144 | # puts msg
145 | # end
146 | # ```
147 | def on_all(&block : String -> Nil)
148 | @all_callback = block
149 | end
150 |
151 | # Add room join callback
152 | # ```
153 | # client.on_room_join do |room|
154 | # puts room.name
155 | # end
156 | # ```
157 | def on_room_join(&block : Room -> Nil)
158 | @room_join_callback = block
159 | end
160 |
161 | # Add ready callback
162 | # ```
163 | # client.on_ready do |user|
164 | # puts user.display_name
165 | # end
166 | # ```
167 | def on_ready(&block : User -> Nil)
168 | @ready_callback = block
169 | end
170 |
171 | # Add new tokens callback
172 | # ```
173 | # client.on_new_tokens do |token, refresh_token|
174 | # puts token
175 | # end
176 | # ```
177 | def on_new_tokens(&block : String, String -> Nil)
178 | @new_tokens_callback = block
179 | end
180 |
181 | # Add user joined room callback
182 | # ```
183 | # client.on_user_joined_room do |user|
184 | # puts user.display_name
185 | # end
186 | # ```
187 | def on_user_joined_room(&block : User -> Nil)
188 | @user_joined_room_callback = block
189 | end
190 |
191 | # Sends message to whatever room currently in
192 | # ```
193 | # client.send_message "msg"
194 | # ```
195 | def send(message : String)
196 | @message_queue << message
197 | end
198 |
199 | def set_speaking(b : Bool)
200 | @ws.send(
201 | {
202 | "op" => SET_SPEAKER,
203 | "p" => {
204 | "active" => b,
205 | },
206 | "v" => "0.2.0",
207 | "fetchId" => "speaking_res",
208 | }.to_json
209 | )
210 |
211 | @speaking = b
212 | end
213 |
214 | private def ping_loop
215 | spawn do
216 | loop do
217 | @ws.send "ping"
218 | sleep PING_TIMEOUT
219 | end
220 | end
221 | end
222 |
223 | private def message_loop
224 | spawn do
225 | loop do
226 | if @message_queue[0]?
227 | @ws.send(
228 | {
229 | "op" => "chat:send_msg",
230 | "d" => {
231 | "tokens" => Message.encode @message_queue[0],
232 | },
233 | "v" => "0.2.0",
234 | }.to_json
235 | )
236 | @message_queue = @message_queue[1..]
237 | end
238 | sleep Time::Span.new nanoseconds: @delay
239 | end
240 | end
241 | end
242 |
243 | # Join room will join the room associated with a roomId
244 | # ```
245 | # client.join_room "roomid"
246 | # ```
247 | def join_room(roomId : String)
248 | @ws.send(
249 | {
250 | "op" => "room:join",
251 | "p" => {
252 | "roomId" => roomId,
253 | },
254 | "ref" => "join_response",
255 | "v" => "0.2.0",
256 | }.to_json
257 | )
258 | @joined_room = true
259 | end
260 |
261 | # Ask to speak will request to speak in whatever room the bot is in
262 | def ask_to_speak
263 | json = JSON.build do |j|
264 | j.object do
265 | j.field "op", ASK_TO_SPEAK
266 | j.field "d" do
267 | j.object do
268 | end
269 | end
270 | end
271 | end
272 |
273 | @ws.send(json)
274 | end
275 |
276 | # Will toggle mute on and off
277 | def toggle_mute
278 | @ws.send(
279 | {
280 | "op" => MUTE,
281 | "d" => {
282 | "value" => !@muted,
283 | },
284 | }.to_json
285 | )
286 |
287 | @muted = !@muted
288 | end
289 |
290 | # Will mute
291 | def mute
292 | @ws.send(
293 | {
294 | "op" => MUTE,
295 | "d" => {
296 | "value" => true,
297 | },
298 | }.to_json
299 | )
300 |
301 | @muted = true
302 | end
303 |
304 | # Will unmute
305 | def unmute
306 | @ws.send(
307 | {
308 | "op" => MUTE,
309 | "d" => {
310 | "value" => false,
311 | },
312 | }.to_json
313 | )
314 |
315 | @muted = false
316 | end
317 |
318 | def get_rooms
319 | @ws.send(
320 | {
321 | "op" => TOP_ROOMS,
322 | "p" => {
323 | "data" => 0,
324 | },
325 | "version" => "0.2.0",
326 | "ref" => "room_response",
327 | }.to_json
328 | )
329 | end
330 |
331 | # Will create room with given name description and privacy option
332 | # Privacy options are limited to "public" and "private"
333 | def create_room(
334 | name : String,
335 | description : String = "",
336 | privacy : String = "public"
337 | )
338 | @ws.send(
339 | {
340 | "op" => CREATE_ROOM,
341 | "p" => {
342 | "name" => name,
343 | "description" => description,
344 | "privacy" => privacy,
345 | },
346 | "version" => "0.2.0",
347 | "ref" => "[uuid]",
348 | }.to_json
349 | )
350 | end
351 |
352 | private def room_loop
353 | spawn do
354 | loop do
355 | get_rooms
356 | sleep 2
357 | end
358 | end
359 | end
360 |
361 | private def setup_run
362 | room_loop
363 |
364 | @ws.on_message do |msg|
365 | if !@all_callback.nil?
366 | @all_callback.not_nil!.call msg
367 | end
368 |
369 | if msg == "pong"
370 | next
371 | end
372 |
373 | msg_json = Hash(String, JSON::Any).from_json msg
374 | if msg_json["op"]?
375 | if msg_json["op"] == "auth-good"
376 | m = msg_json["d"]
377 | .as_h["user"]
378 | .as_h
379 | user = User.from_json(m)
380 | if !@ready_callback.nil?
381 | @ready_callback.not_nil!.call user
382 | end
383 | @myself = user
384 | elsif msg_json["op"] == "room:get_top:reply"
385 | @rooms = msg_json["p"]
386 | .as_h["rooms"]
387 | .as_a.map { |room| Room.from_json room.as_h }
388 | elsif msg_json["op"] == "new_user_join_room"
389 | m = msg_json["d"]
390 | .as_h["user"]
391 | .as_h
392 |
393 | if !@user_joined_room_callback.nil?
394 | @user_joined_room_callback.not_nil!.call User.from_json m
395 | end
396 | elsif msg_json["op"] == "new-tokens"
397 | m = msg_json["d"]
398 | .as_h
399 |
400 | if !@new_tokens_callback.nil?
401 | @new_tokens_callback.not_nil!.call(
402 | m["accessToken"].as_s,
403 | m["refreshToken"].as_s
404 | )
405 | end
406 | elsif msg_json["op"] == "new_chat_msg"
407 | if !@message_callback.nil?
408 | m = msg_json["d"]
409 | .as_h["msg"]
410 | .as_h
411 | @message_callback.not_nil!.call(
412 | Message.new(
413 | m["userId"].as_s,
414 | m["sentAt"].as_s,
415 | m["isWhisper"].as_bool,
416 | Message.decode(
417 | msg_json["d"]
418 | .as_h["msg"]
419 | .as_h["tokens"]
420 | .as_a.map &.as_h
421 | )
422 | )
423 | )
424 | end
425 | elsif msg_json["op"] == "room-created"
426 | join_room msg_json["d"].as_h["roomId"].as_s
427 | elsif msg_json["op"] == "room:join:reply"
428 | payload = msg_json["p"]
429 | .as_h
430 | if !@room_join_callback.nil?
431 | @room_join_callback.not_nil!.call Room.from_json payload
432 | end
433 | # @delay = payload["chatThrottle"].as_i * 1000000
434 | @in_room = true
435 | end
436 | end
437 | end
438 |
439 | ping_loop
440 | message_loop
441 | end
442 |
443 | # Run the client
444 | # This will start the message loop
445 | def run
446 | setup_run
447 |
448 | @ws.run
449 | end
450 | end
451 |
--------------------------------------------------------------------------------
/src/galileo/message.cr:
--------------------------------------------------------------------------------
1 | class Galileo::Message
2 | getter user_id : String
3 | getter sent_at : String
4 | getter is_whisper : Bool
5 | getter content : String
6 |
7 | def initialize(
8 | @user_id : String,
9 | @sent_at : String,
10 | @is_whisper : Bool,
11 | @content : String
12 | )
13 | end
14 |
15 | def self.encode(message : String)
16 | block_started = false
17 |
18 | message.split(" ").map do |word|
19 | if word.starts_with? "@"
20 | {type: "mention", value: word[1..]}
21 | elsif word.starts_with? "https"
22 | {type: "link", value: word}
23 | elsif word.starts_with? "`"
24 | if word.ends_with? "`"
25 | {type: "block", value: word[1..word[1..].rindex("`")]}
26 | else
27 | block_started = true
28 | {type: "block", value: word[1..]}
29 | end
30 | elsif word.ends_with? "`"
31 | block_started = false
32 | {type: "block", value: word[..word.rindex("`").not_nil! - 1]}
33 | elsif word.starts_with?(":") && word.ends_with? ":"
34 | {type: "emote", value: word[1..word.size]}
35 | else
36 | if block_started
37 | {type: "block", value: word}
38 | else
39 | {type: "text", value: word}
40 | end
41 | end
42 | end
43 | end
44 |
45 | def self.decode(message : Array(Hash(String, JSON::Any))) : String
46 | message.map do |word|
47 | if word["t"] == "mention"
48 | "@#{word["v"].as_s}"
49 | elsif word["t"] == "block"
50 | "`#{word["v"].as_s}`"
51 | elsif word["t"] == "emote"
52 | ":#{word["v"].as_s}:"
53 | else
54 | word["v"].as_s
55 | end
56 | end.join " "
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/src/galileo/ops.cr:
--------------------------------------------------------------------------------
1 | AUTH_GOOD = "auth-good"
2 | NEW_TOKENS = "new-tokens"
3 | TOP_ROOMS = "room:get_top"
4 |
5 | ERROR = "error"
6 |
7 | BOT_JOINED_AS_SPEAKER = "you-joined-as-speaker"
8 | BOT_JOINED_AS_PEER = "you-joined-as-peer"
9 | BOT_LEFT_ROOM = "you_left_room"
10 | SET_SPEAKER = "room:set_active_speaker"
11 |
12 | BOT_IS_NOW_SPEAKER = "you-are-now-a-speaker"
13 |
14 | NEW_PEER_SPEAKER = "new-peer-speaker"
15 |
16 | JOIN_ROOM = "join_room"
17 |
18 | ACTIVE_SPEAKER_CHANGE = "active_speaker_change"
19 |
20 | USER_LEFT_ROOM = "user_left_room"
21 | USER_JOINED_ROOM = "new_user_join_room"
22 |
23 | NEW_CHAT_MESSAGE = "new_chat_msg"
24 |
25 | MOD_CHANGED = "mod_changed"
26 | SPEAKER_REMOVED = "speaker_removed"
27 | CHAT_USER_BANNED = "chat_user_banned"
28 |
29 | ASK_TO_SPEAK = "ask_to_speak"
30 | HAND_RAISED = "hand_raised"
31 | ADD_SPEAKER = "add_speaker"
32 | SET_LISTENER = "set_listener"
33 | SET_ROLE = "room:set_role"
34 |
35 | GET_CURRENT_ROOM_USERS = "get_current_room_users"
36 | GET_CURRENT_ROOM_USERS_DONE = "get_current_room_users_done"
37 | CREATE_ROOM = "room:create"
38 |
39 | SEND_ROOM_CHAT_MSG = "send_room_chat_msg"
40 |
41 | DELETE_ROOM_CHAT_MESSAGE = "delete_room_chat_message"
42 |
43 | MUTE = "mute"
44 | FOLLOW = "follow"
45 |
46 | CHANGE_MOD_STATUS = "change_mod_status"
47 |
48 | GET_TOP_PUBLIC_ROOMS = "get_top_public_rooms"
49 | GET_TOP_PUBLIC_ROOMS_DONE = "get_top_public_rooms_done"
50 |
51 | GET_USER_PROFILE = "get_user_profile"
52 |
53 | FETCH_DONE = "fetch_done"
54 |
55 | JOIN_ROOM_DONE = "join_room_done"
56 | GET_FOLLOW_LIST = "get_follow_list"
57 |
--------------------------------------------------------------------------------
/src/galileo/room.cr:
--------------------------------------------------------------------------------
1 | require "json"
2 |
3 | class Galileo::Room
4 | property id : String
5 | property name : String
6 | property description : String
7 | property is_private : Bool
8 |
9 | def initialize(
10 | @id : String,
11 | @name : String,
12 | @description : String,
13 | @is_private : Bool
14 | )
15 | end
16 |
17 | def self.from_json(payload : Hash(String, JSON::Any))
18 | Room.new(
19 | payload["id"].as_s,
20 | payload["name"].as_s,
21 | payload["description"].as_s,
22 | payload["isPrivate"].as_bool,
23 | )
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/src/galileo/user.cr:
--------------------------------------------------------------------------------
1 | require "http/web_socket"
2 | require "json"
3 |
4 | class Galileo::User
5 | getter id : String
6 | getter username : String
7 | getter avatar_url : String
8 | # If there is no banner url given this will be an empty string
9 | getter banner_url : String
10 | getter bio : String
11 | getter online : Bool
12 | getter staff : Bool
13 | getter last_online : String
14 | # If there is no current room id given this will be an empty string
15 | getter current_room_id : String
16 | getter display_name : String
17 | getter num_following : Int32
18 | getter num_followers : Int32
19 | getter contributions : Int32
20 | getter you_are_following : Bool
21 | getter follows_you : Bool
22 | # If user is not a bot this will be an empty string
23 | getter bot_owner_id : String
24 |
25 | def initialize(
26 | @id : String,
27 | @username : String,
28 | @avatar_url : String,
29 | @banner_url : String,
30 | @bio : String,
31 | @online : Bool,
32 | @staff : Bool,
33 | @last_online : String,
34 | @current_room_id : String,
35 | @display_name : String,
36 | @num_following : Int32,
37 | @num_followers : Int32,
38 | @contributions : Int32,
39 | @you_are_following : Bool,
40 | @follows_you : Bool,
41 | @bot_owner_id : String
42 | )
43 | end
44 |
45 | def self.from_json(m : Hash(String, JSON::Any)) : User
46 | User.new(
47 | m["id"].as_s,
48 | m["username"].as_s,
49 | m["avatarUrl"].as_s,
50 | m["bannerUrl"].as_s? ? m["bannerUrl"].as_s : "",
51 | m["bio"].as_s? ? m["bio"].as_s : "",
52 | m["online"].as_bool,
53 | m["staff"].as_bool,
54 | m["lastOnline"].as_s,
55 | m["currentRoomId"].as_s? ? m["currentRoomId"].as_s : "",
56 | m["displayName"].as_s,
57 | m["numFollowing"].as_i,
58 | m["numFollowers"].as_i,
59 | m["contributions"].as_i,
60 | m["youAreFollowing"].as_bool? ? true : false,
61 | m["followsYou"].as_bool? ? true : false,
62 | m["botOwnerId"].as_s? ? m["botOwnerId"].as_s : ""
63 | )
64 | end
65 | end
66 |
--------------------------------------------------------------------------------