├── .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 |

7 | Examples 8 | · 9 | Documentation 10 | · 11 | Guide 12 |

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 | 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 |

127 | 128 | 131 | 132 | Direct Known Subclasses 133 |

134 | 141 | 142 | 143 | 144 | 145 | 146 | 147 |

148 | 149 | 152 | 153 | Defined in: 154 |

155 | 156 | 157 | dogehouse_cr/entities/base.cr 158 | 159 |
160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 |

171 | 172 | 175 | 176 | Instance Method Summary 177 |

178 | 195 | 196 | 197 | 198 | 199 | 200 |
201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 |
223 | 224 | 225 | 226 | 227 | 228 | 229 |

230 | 231 | 234 | 235 | Instance Method Detail 236 |

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 | 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 |

132 | 133 | 136 | 137 | Defined in: 138 |

139 | 140 | 141 | dogehouse_cr/entities/context.cr 142 | 143 |
144 | 145 | 146 | 147 | 148 | 149 | 150 |

151 | 152 | 155 | 156 | Constructors 157 |

158 | 166 | 167 | 168 | 169 | 170 | 171 |

172 | 173 | 176 | 177 | Instance Method Summary 178 |

179 | 192 | 193 | 194 | 195 | 196 | 197 |
198 | 199 | 200 | 201 |

Instance methods inherited from class BaseEntity

202 | 203 | 204 | 205 | join_room(roomId : String) 206 | join_room, 207 | 208 | 209 | 210 | send_message(message : String) 211 | send_message 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 |
244 | 245 | 246 |

247 | 248 | 251 | 252 | Constructor Detail 253 |

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 |

275 | 276 | 279 | 280 | Instance Method Detail 281 |

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 | 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 |

122 | 123 | 126 | 127 | Direct Known Subclasses 128 |

129 | 136 | 137 | 138 | 139 | 140 | 141 | 142 |

143 | 144 | 147 | 148 | Defined in: 149 |

150 | 151 | 152 | dogehouse_cr/entity.cr 153 | 154 |
155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 |

166 | 167 | 170 | 171 | Instance Method Summary 172 |

173 | 190 | 191 | 192 | 193 | 194 | 195 |
196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 |
218 | 219 | 220 | 221 | 222 | 223 | 224 |

225 | 226 | 229 | 230 | Instance Method Detail 231 |

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 | 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 |

120 | 121 | 124 | 125 | Defined in: 126 |

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 |

144 | 145 | 148 | 149 | Constructors 150 |

151 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 |
171 | 172 |
173 | 174 | 175 |

176 | 177 | 180 | 181 | Constructor Detail 182 |

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 | 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 |

122 | 123 | 126 | 127 | Defined in: 128 |

129 | 130 | 131 | dogehouse_cr/entities/message.cr 132 | 133 |
134 | 135 | 136 | 137 | 138 | 139 | 140 |

141 | 142 | 145 | 146 | Constructors 147 |

148 | 156 | 157 | 158 | 159 | 160 | 161 |

162 | 163 | 166 | 167 | Instance Method Summary 168 |

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 |

223 | 224 | 227 | 228 | Constructor Detail 229 |

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 |

251 | 252 | 255 | 256 | Instance Method Detail 257 |

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 | 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 |

122 | 123 | 126 | 127 | Defined in: 128 |

129 | 130 | 131 | dogehouse_cr/entities/room.cr 132 | 133 |
134 | 135 | 136 | 137 | 138 | 139 | 140 |

141 | 142 | 145 | 146 | Constructors 147 |

148 | 156 | 157 | 158 | 159 | 160 | 161 |

162 | 163 | 166 | 167 | Instance Method Summary 168 |

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 |

223 | 224 | 227 | 228 | Constructor Detail 229 |

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 |

251 | 252 | 255 | 256 | Instance Method Detail 257 |

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 | 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 |

122 | 123 | 126 | 127 | Defined in: 128 |

129 | 130 | 131 | dogehouse_cr/entities/user.cr 132 | 133 |
134 | 135 | 136 | 137 | 138 | 139 | 140 |

141 | 142 | 145 | 146 | Constructors 147 |

148 | 161 | 162 | 163 | 164 | 165 | 166 |

167 | 168 | 171 | 172 | Instance Method Summary 173 |

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 |

294 | 295 | 298 | 299 | Constructor Detail 300 |

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 |

336 | 337 | 340 | 341 | Instance Method Detail 342 |

343 | 344 |
345 |
346 | 347 | def avatar_url : String 348 | 349 | # 350 |
351 | 352 |
353 |
354 | 355 |
356 |
357 | 358 | 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 | 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 |

120 | 121 | 124 | 125 | Defined in: 126 |

127 | 128 | 129 | galileo.cr 130 | 131 |
132 | 133 | 134 | galileo/message.cr 135 | 136 |
137 | 138 | 139 | 140 | 141 | 142 | 143 |

144 | 145 | 148 | 149 | Constructors 150 |

151 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 |
171 | 172 |
173 | 174 | 175 |

176 | 177 | 180 | 181 | Constructor Detail 182 |

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 | 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 |

122 | 123 | 126 | 127 | Defined in: 128 |

129 | 130 | 131 | galileo/message.cr 132 | 133 |
134 | 135 | 136 | 137 | 138 | 139 | 140 |

141 | 142 | 145 | 146 | Constructors 147 |

148 | 156 | 157 | 158 | 159 |

160 | 161 | 164 | 165 | Class Method Summary 166 |

167 | 180 | 181 | 182 | 183 |

184 | 185 | 188 | 189 | Instance Method Summary 190 |

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 |

245 | 246 | 249 | 250 | Constructor Detail 251 |

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 |

271 | 272 | 275 | 276 | Class Method Detail 277 |

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 |

311 | 312 | 315 | 316 | Instance Method Detail 317 |

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 | 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 |

122 | 123 | 126 | 127 | Defined in: 128 |

129 | 130 | 131 | galileo/room.cr 132 | 133 |
134 | 135 | 136 | 137 | 138 | 139 | 140 |

141 | 142 | 145 | 146 | Constructors 147 |

148 | 156 | 157 | 158 | 159 |

160 | 161 | 164 | 165 | Class Method Summary 166 |

167 | 175 | 176 | 177 | 178 |

179 | 180 | 183 | 184 | Instance Method Summary 185 |

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 |

260 | 261 | 264 | 265 | Constructor Detail 266 |

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 |

286 | 287 | 290 | 291 | Class Method Detail 292 |

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 |

312 | 313 | 316 | 317 | Instance Method Detail 318 |

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 | 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 |

122 | 123 | 126 | 127 | Defined in: 128 |

129 | 130 | 131 | galileo/user.cr 132 | 133 |
134 | 135 | 136 | 137 | 138 | 139 | 140 |

141 | 142 | 145 | 146 | Constructors 147 |

148 | 161 | 162 | 163 | 164 | 165 | 166 |

167 | 168 | 171 | 172 | Instance Method Summary 173 |

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 |

294 | 295 | 298 | 299 | Constructor Detail 300 |

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 |

336 | 337 | 340 | 341 | Instance Method Detail 342 |

343 | 344 |
345 |
346 | 347 | def avatar_url : String 348 | 349 | # 350 |
351 | 352 |
353 |
354 | 355 |
356 |
357 | 358 | 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 | 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 |

127 | 128 | 131 | 132 | Defined in: 133 |

134 | 135 | 136 | dogehouse_cr/entity.cr 137 | 138 |
139 | 140 | 141 | 142 | 143 | 144 | 145 |

146 | 147 | 150 | 151 | Constructors 152 |

153 | 161 | 162 | 163 | 164 | 165 | 166 |

167 | 168 | 171 | 172 | Instance Method Summary 173 |

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 |

228 | 229 | 232 | 233 | Constructor Detail 234 |

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 |

256 | 257 | 260 | 261 | Instance Method Detail 262 |

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 | 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 | '' 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 | 28 | 93 | 94 | 95 |
96 |

97 | 98 | Top Level Namespace 99 | 100 |

101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 |

111 | 112 | 115 | 116 | Included Modules 117 |

118 | 125 | 126 | 127 | 128 |

129 | 130 | 133 | 134 | Extended Modules 135 |

136 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 |

152 | 153 | 156 | 157 | Defined in: 158 |

159 | 160 | 161 | 162 | 163 | 164 |

165 | 166 | 169 | 170 | Constant Summary 171 |

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 | --------------------------------------------------------------------------------