· 5 years ago · Jan 19, 2021, 08:40 PM
1-- This code is licensed under the MIT Open Source License.
2
3-- Copyright (c) 2015 Ruairidh Carmichael - ruairidhcarmichael@live.co.uk
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
23--local path = (...):match('(.-)[^%.]+$')
24
25 _VERSION = '0.0.1'
26 _DESCRIPTION = 'Lua API Wrapper for Discord'
27
28
29--if type(client) ~= "table" then os.loadAPI('/discord/client') end
30--if type(message) ~= "table" then os.loadAPI('/discord/message') end
31
32--Client = client.Client
33--Message = message.Message
34
35-- Amalgamated source below
36
37-- class.lua
38
39middleclass = {
40 _VERSION = 'middleclass v3.1.0',
41 _DESCRIPTION = 'Object Orientation for Lua',
42 _URL = 'https://github.com/kikito/middleclass',
43 _LICENSE = [[
44 MIT LICENSE
45
46 Copyright (c) 2011 Enrique García Cota
47
48 Permission is hereby granted, free of charge, to any person obtaining a
49 copy of this software and associated documentation files (the
50 "Software"), to deal in the Software without restriction, including
51 without limitation the rights to use, copy, modify, merge, publish,
52 distribute, sublicense, and/or sell copies of the Software, and to
53 permit persons to whom the Software is furnished to do so, subject to
54 the following conditions:
55
56 The above copyright notice and this permission notice shall be included
57 in all copies or substantial portions of the Software.
58
59 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
60 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
61 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
62 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
63 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
64 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
65 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
66 ]]
67}
68
69local function _setClassDictionariesMetatables(aClass)
70 local dict = aClass.__instanceDict
71 dict.__index = dict
72
73 local super = aClass.super
74 if super then
75 local superStatic = super.static
76 setmetatable(dict, super.__instanceDict)
77 setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) or superStatic[k] end })
78 else
79 setmetatable(aClass.static, { __index = function(_,k) return dict[k] end })
80 end
81end
82
83local function _setClassMetatable(aClass)
84 setmetatable(aClass, {
85 __tostring = function() return "class " .. aClass.name end,
86 __index = aClass.static,
87 __newindex = aClass.__instanceDict,
88 __call = function(self, ...) return self:new(...) end
89 })
90end
91
92local function _createClass(name, super)
93 local aClass = { name = name, super = super, static = {}, __mixins = {}, __instanceDict={} }
94 aClass.subclasses = setmetatable({}, {__mode = "k"})
95
96 _setClassDictionariesMetatables(aClass)
97 _setClassMetatable(aClass)
98
99 return aClass
100end
101
102local function _createLookupMetamethod(aClass, name)
103 return function(...)
104 local method = aClass.super[name]
105 assert( type(method)=='function', tostring(aClass) .. " doesn't implement metamethod '" .. name .. "'" )
106 return method(...)
107 end
108end
109
110local function _setClassMetamethods(aClass)
111 for _,m in ipairs(aClass.__metamethods) do
112 aClass[m]= _createLookupMetamethod(aClass, m)
113 end
114end
115
116local function _setDefaultInitializeMethod(aClass, super)
117 aClass.initialize = function(instance, ...)
118 return super.initialize(instance, ...)
119 end
120end
121
122local function _includeMixin(aClass, mixin)
123 assert(type(mixin)=='table', "mixin must be a table")
124 for name,method in pairs(mixin) do
125 if name ~= "included" and name ~= "static" then aClass[name] = method end
126 end
127 if mixin.static then
128 for name,method in pairs(mixin.static) do
129 aClass.static[name] = method
130 end
131 end
132 if type(mixin.included)=="function" then mixin:included(aClass) end
133 aClass.__mixins[mixin] = true
134end
135
136local Object = _createClass("Object", nil)
137
138Object.static.__metamethods = { '__add', '__band', '__bor', '__bxor', '__bnot', '__call', '__concat',
139 '__div', '__eq', '__ipairs', '__idiv', '__le', '__len', '__lt', '__mod',
140 '__mul', '__pairs', '__pow', '__shl', '__shr', '__sub', '__tostring', '__unm' }
141
142function Object.static:allocate()
143 assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'")
144 return setmetatable({ class = self }, self.__instanceDict)
145end
146
147function Object.static:new(...)
148 local instance = self:allocate()
149 instance:initialize(...)
150 return instance
151end
152
153function Object.static:subclass(name)
154 assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'")
155 assert(type(name) == "string", "You must provide a name(string) for your class")
156
157 local subclass = _createClass(name, self)
158 _setClassMetamethods(subclass)
159 _setDefaultInitializeMethod(subclass, self)
160 self.subclasses[subclass] = true
161 self:subclassed(subclass)
162
163 return subclass
164end
165
166function Object.static:subclassed(other) end
167
168function Object.static:isSubclassOf(other)
169 return type(other) == 'table' and
170 type(self) == 'table' and
171 type(self.super) == 'table' and
172 ( self.super == other or
173 type(self.super.isSubclassOf) == 'function' and
174 self.super:isSubclassOf(other)
175 )
176end
177
178function Object.static:include( ... )
179 assert(type(self) == 'table', "Make sure you that you are using 'Class:include' instead of 'Class.include'")
180 for _,mixin in ipairs({...}) do _includeMixin(self, mixin) end
181 return self
182end
183
184function Object.static:includes(mixin)
185 return type(mixin) == 'table' and
186 type(self) == 'table' and
187 type(self.__mixins) == 'table' and
188 ( self.__mixins[mixin] or
189 type(self.super) == 'table' and
190 type(self.super.includes) == 'function' and
191 self.super:includes(mixin)
192 )
193end
194
195function Object:initialize() end
196
197function Object:__tostring() return "instance of " .. tostring(self.class) end
198
199function Object:isInstanceOf(aClass)
200 return type(self) == 'table' and
201 type(self.class) == 'table' and
202 type(aClass) == 'table' and
203 ( aClass == self.class or
204 type(aClass.isSubclassOf) == 'function' and
205 self.class:isSubclassOf(aClass)
206 )
207end
208
209
210
211local function class(name, super, ...)
212 super = super or Object
213 return super:subclass(name, ...)
214end
215
216middleclass.Object = Object
217
218setmetatable(middleclass, { __call = function(_, ...) return middleclass.class(...) end })
219
220
221-- util.lua
222
223function responseIsSuccessful(response)
224 return response.code ~= nil and response.code >= 200 and response.code < 300
225end
226
227-- endpoints.lua
228
229local endpoints = {}
230endpoints.base = 'https://discordapp.com'
231endpoints.apiBase = endpoints.base .. '/api'
232endpoints.gateway = endpoints.apiBase .. '/gateway'
233endpoints.users = endpoints.apiBase .. '/users'
234endpoints.register = endpoints.apiBase .. '/auth/register'
235endpoints.login = endpoints.apiBase .. '/auth/login'
236endpoints.logout = endpoints.apiBase .. '/auth/logout'
237endpoints.servers = endpoints.apiBase .. '/guilds'
238endpoints.channels = endpoints.apiBase .. '/channels'
239
240-- json.lua
241
242--
243-- json.lua
244--
245-- Copyright (c) 2015 rxi
246--
247-- This library is free software; you can redistribute it and/or modify it
248-- under the terms of the MIT license. See LICENSE for details.
249--
250
251_version = "0.1.0"
252
253-- Encode
254
255local encode
256
257local escape_char_map = {
258 [ "\\" ] = "\\\\",
259 [ "\"" ] = "\\\"",
260 [ "\b" ] = "\\b",
261 [ "\f" ] = "\\f",
262 [ "\n" ] = "\\n",
263 [ "\r" ] = "\\r",
264 [ "\t" ] = "\\t",
265}
266
267local escape_char_map_inv = { [ "\\/" ] = "/" }
268for k, v in pairs(escape_char_map) do
269 escape_char_map_inv[v] = k
270end
271
272
273local function escape_char(c)
274 return escape_char_map[c] or string.format("\\u%04x", c:byte())
275end
276
277
278local function encode_nil(val)
279 return "null"
280end
281
282
283local function encode_table(val, stack)
284 local res = {}
285 stack = stack or {}
286
287 -- Circular reference?
288 if stack[val] then error("circular reference") end
289
290 stack[val] = true
291
292 if val[1] ~= nil or next(val) == nil then
293 -- Treat as array -- check keys are valid and it is not sparse
294 local n = 0
295 for k in pairs(val) do
296 if type(k) ~= "number" then
297 error("invalid table: mixed or invalid key types")
298 end
299 n = n + 1
300 end
301 if n ~= #val then
302 error("invalid table: sparse array")
303 end
304 -- Encode
305 for i, v in ipairs(val) do
306 table.insert(res, encode(v, stack))
307 end
308 stack[val] = nil
309 return "[" .. table.concat(res, ",") .. "]"
310
311 else
312 -- Treat as an object
313 for k, v in pairs(val) do
314 if type(k) ~= "string" then
315 error("invalid table: mixed or invalid key types")
316 end
317 table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
318 end
319 stack[val] = nil
320 return "{" .. table.concat(res, ",") .. "}"
321 end
322end
323
324
325local function encode_string(val)
326 return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
327end
328
329
330local function encode_number(val)
331 -- Check for NaN, -inf and inf
332 if val ~= val or val <= -math.huge or val >= math.huge then
333 error("unexpected number value '" .. tostring(val) .. "'")
334 end
335 return string.format("%.14g", val)
336end
337
338
339local type_func_map = {
340 [ "nil" ] = encode_nil,
341 [ "table" ] = encode_table,
342 [ "string" ] = encode_string,
343 [ "number" ] = encode_number,
344 [ "boolean" ] = tostring,
345}
346
347
348local encode = function(val, stack)
349 local t = type(val)
350 local f = type_func_map[t]
351 if f then
352 return f(val, stack)
353 end
354 error("unexpected type '" .. t .. "'")
355end
356
357
358local function encode(val)
359 return ( encode(val) )
360end
361
362-- Decode
363
364local parse
365
366local function create_set(...)
367 local res = {}
368 for i = 1, select("#", ...) do
369 res[ select(i, ...) ] = true
370 end
371 return res
372end
373
374local space_chars = create_set(" ", "\t", "\r", "\n")
375local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
376local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
377local literals = create_set("true", "false", "null")
378
379local literal_map = {
380 [ "true" ] = true,
381 [ "false" ] = false,
382 [ "null" ] = nil,
383}
384
385
386local function next_char(str, idx, set, negate)
387 for i = idx, #str do
388 if set[str:sub(i, i)] ~= negate then
389 return i
390 end
391 end
392 return #str + 1
393end
394
395
396local function decode_error(str, idx, msg)
397 local line_count = 1
398 local col_count = 1
399 for i = 1, idx - 1 do
400 col_count = col_count + 1
401 if str:sub(i, i) == "\n" then
402 line_count = line_count + 1
403 col_count = 1
404 end
405 end
406 error( string.format("%s at line %d col %d", msg, line_count, col_count) )
407end
408
409
410local function codepoint_to_utf8(n)
411 -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
412 local f = math.floor
413 if n <= 0x7f then
414 return string.char(n)
415 elseif n <= 0x7ff then
416 return string.char(f(n / 64) + 192, n % 64 + 128)
417 elseif n <= 0xffff then
418 return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
419 elseif n <= 0x10ffff then
420 return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
421 f(n % 4096 / 64) + 128, n % 64 + 128)
422 end
423 error( string.format("invalid unicode codepoint '%x'", n) )
424end
425
426
427local function parse_unicode_escape(s)
428 local n1 = tonumber( s:sub(3, 6), 16 )
429 local n2 = tonumber( s:sub(9, 12), 16 )
430 -- Surrogate pair?
431 if n2 then
432 return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
433 else
434 return codepoint_to_utf8(n1)
435 end
436end
437
438
439local function parse_string(str, i)
440 local has_unicode_escape = false
441 local has_surrogate_escape = false
442 local has_escape = false
443 local last
444 for j = i + 1, #str do
445 local x = str:byte(j)
446
447 if x < 32 then
448 decode_error(str, j, "control character in string")
449 end
450
451 if last == 92 then -- "\\" (escape char)
452 if x == 117 then -- "u" (unicode escape sequence)
453 local hex = str:sub(j + 1, j + 5)
454 if not hex:find("%x%x%x%x") then
455 decode_error(str, j, "invalid unicode escape in string")
456 end
457 if hex:find("^[dD][89aAbB]") then
458 has_surrogate_escape = true
459 else
460 has_unicode_escape = true
461 end
462 else
463 local c = string.char(x)
464 if not escape_chars[c] then
465 decode_error(str, j, "invalid escape char '" .. c .. "' in string")
466 end
467 has_escape = true
468 end
469 last = nil
470
471 elseif x == 34 then -- '"' (end of string)
472 local s = str:sub(i + 1, j - 1)
473 if has_surrogate_escape then
474 s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
475 end
476 if has_unicode_escape then
477 s = s:gsub("\\u....", parse_unicode_escape)
478 end
479 if has_escape then
480 s = s:gsub("\\.", escape_char_map_inv)
481 end
482 return s, j + 1
483
484 else
485 last = x
486 end
487 end
488 decode_error(str, i, "expected closing quote for string")
489end
490
491
492local function parse_number(str, i)
493 local x = next_char(str, i, delim_chars)
494 local s = str:sub(i, x - 1)
495 local n = tonumber(s)
496 if not n then
497 decode_error(str, i, "invalid number '" .. s .. "'")
498 end
499 return n, x
500end
501
502
503local function parse_literal(str, i)
504 local x = next_char(str, i, delim_chars)
505 local word = str:sub(i, x - 1)
506 if not literals[word] then
507 decode_error(str, i, "invalid literal '" .. word .. "'")
508 end
509 return literal_map[word], x
510end
511
512
513local function parse_array(str, i)
514 local res = {}
515 local n = 1
516 i = i + 1
517 while 1 do
518 local x
519 i = next_char(str, i, space_chars, true)
520 -- Empty / end of array?
521 if str:sub(i, i) == "]" then
522 i = i + 1
523 break
524 end
525 -- Read token
526 x, i = parse(str, i)
527 res[n] = x
528 n = n + 1
529 -- Next token
530 i = next_char(str, i, space_chars, true)
531 local chr = str:sub(i, i)
532 i = i + 1
533 if chr == "]" then break end
534 if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
535 end
536 return res, i
537end
538
539
540local function parse_object(str, i)
541 local res = {}
542 i = i + 1
543 while 1 do
544 local key, val
545 i = next_char(str, i, space_chars, true)
546 -- Empty / end of object?
547 if str:sub(i, i) == "}" then
548 i = i + 1
549 break
550 end
551 -- Read key
552 if str:sub(i, i) ~= '"' then
553 decode_error(str, i, "expected string for key")
554 end
555 key, i = parse(str, i)
556 -- Read ':' delimiter
557 i = next_char(str, i, space_chars, true)
558 if str:sub(i, i) ~= ":" then
559 decode_error(str, i, "expected ':' after key")
560 end
561 i = next_char(str, i + 1, space_chars, true)
562 -- Read value
563 val, i = parse(str, i)
564 -- Set
565 res[key] = val
566 -- Next token
567 i = next_char(str, i, space_chars, true)
568 local chr = str:sub(i, i)
569 i = i + 1
570 if chr == "}" then break end
571 if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
572 end
573 return res, i
574end
575
576
577local char_func_map = {
578 [ '"' ] = parse_string,
579 [ "0" ] = parse_number,
580 [ "1" ] = parse_number,
581 [ "2" ] = parse_number,
582 [ "3" ] = parse_number,
583 [ "4" ] = parse_number,
584 [ "5" ] = parse_number,
585 [ "6" ] = parse_number,
586 [ "7" ] = parse_number,
587 [ "8" ] = parse_number,
588 [ "9" ] = parse_number,
589 [ "-" ] = parse_number,
590 [ "t" ] = parse_literal,
591 [ "f" ] = parse_literal,
592 [ "n" ] = parse_literal,
593 [ "[" ] = parse_array,
594 [ "{" ] = parse_object,
595}
596
597
598parse = function(str, idx)
599 local chr = str:sub(idx, idx)
600 local f = char_func_map[chr]
601 if f then
602 return f(str, idx)
603 end
604 decode_error(str, idx, "unexpected character '" .. chr .. "'")
605end
606
607
608local function decode(str)
609 if type(str) ~= "string" then
610 error("expected argument of type string, got " .. type(str))
611 end
612 return ( parse(str, next_char(str, 1, space_chars, true)) )
613end
614
615
616-- wrapper.lua
617
618local function send(endpoint, method, data, headers)
619
620 local retval = {}
621 headers["User-Agent"] = "Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)"
622 if method == "GET" then
623 local handle = http.get(endpoint, headers)
624 if handle == nil then
625 print("Error in GET request to " .. endpoint)
626 return {}
627 end
628 retval.body = handle.readAll()
629 retval.headers = headers
630 retval.set_cookies = {}
631 retval.code = handle.getResponseCode()
632 retval.raw_headers = headers
633 --handle.close()
634 handle = nil
635 return retval
636 elseif method == "POST" then
637 local handle = http.post(endpoint, textutils.serializeJSON(data), headers)
638 if handle == nil then
639 print("Error in POST request to " .. endpoint)
640 return {}
641 end
642 local status, newdata = pcall(handle.readAll)
643 if not status then
644 print(newdata)
645 return
646 end
647 retval.body = newdata
648 retval.headers = headers
649 retval.set_cookies = {}
650 retval.code = handle.getResponseCode()
651 retval.raw_headers = headers
652 --handle.close()
653 handle = nil
654 return retval
655 else
656 print("Incorrect method " .. method .. " given")
657 return {}
658 end
659end
660
661-- message.lua
662
663-------------------------------------
664-- Discord Message Class
665-- @module Message
666
667-- local path = (...):match('(.-)[^%.]+$')
668
669--if type(wrapper) ~= "table" then os.loadAPI('/discord/wrapper') end
670--local request = wrapper
671--if type(class) ~= "table" then os.loadAPI('/discord/class') end
672--if type(json) ~= "table" then os.loadAPI('/discord/json') end
673--if type(endpoints) ~= "table" then os.loadAPI('/discord/endpoints') end
674--if type(util) ~= "table" then os.loadAPI('/discord/util') end
675-- if type() ~= "table" then os.loadAPI('/discord/') end
676
677Message = class('MessageObject')
678
679--- Internally initialize's the Message class.
680--- Please use Message:new(packet, token) instead.
681-- @param packet Packet to be sent as the message.
682-- @param token Authentication token from login.
683function Message:initialize(packet, token)
684
685 self.packet = packet or {}
686 self.token = token or ''
687
688 self.headers = {}
689 self.headers['authorization'] = self.token
690 self.headers['Content-Type'] = 'application/json'
691
692end
693
694--- Edits a sent message.
695-- @param msg The new message to replace the old one.
696-- @return Response Packet received from Discord
697function Message:edit(msg)
698
699 local payload = {
700 content = tostring(msg)
701 }
702
703 local response = send(endpoints.channels .. '/' .. self.packet.channel_id .. '/messages/' .. self.packet.id, 'PATCH', payload, self.headers)
704
705 if responseIsSuccessful(response) then
706 self.packet = decode(response.body)
707 end
708
709 return utssl.responseIsSuccessful(response)
710
711end
712
713--- Deletes a sent message.
714-- @return Response Packet received from Discord
715function Message:delete()
716
717 local response = send(endpoints.channels .. '/' .. self.packet.channel_id .. '/messages/' .. self.packet.id, 'DELETE', nil, self.headers)
718
719 if responseIsSuccessful(response) then
720 self = nil
721 end
722
723 return responseIsSuccessful(response)
724
725end
726
727
728-- client.lua
729
730-- This code is licensed under the MIT Open Source License.
731
732-------------------------------------
733-- Discord Client Class
734-- @module Client
735
736-- local path = (...):match('(.-)[^%.]+$')
737
738--if type(wrapper) ~= "table" then os.loadAPI('/discord/wrapper') end
739--local request = wrapper
740--if type(class) ~= "table" then os.loadAPI('/discord/class') end
741--if type(json) ~= "table" then os.loadAPI('/discord/json') end
742--if type(endpoints) ~= "table" then os.loadAPI('/discord/endpoints') end
743--if type(util) ~= "table" then os.loadAPI('/discord/util') end
744
745--if type(message) ~= "table" then os.loadAPI('/discord/message') end
746
747print('Loaded client')
748
749Client = class('ClientObject')
750
751--- Internally initialize's the Client class.
752--- Please use Client:new(options) instead.
753-- @param options Options table (Currently useless.)
754function Client:initialize(options)
755
756 self.isLoggedIn = false
757 self.options = options
758 self.token = ''
759 self.email = ''
760 self.user = {}
761
762 self.headers = {}
763
764 self.headers['authorization'] = self.token
765 self.headers['Content-Type'] = 'application/json'
766 self.headers['User-Agent'] = 'Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)'
767
768 self.callbacks = {}
769
770 self.socket = nil
771
772end
773
774--- Logs the Client into Discord's servers.
775--- This is required to use most of the Clients functions.
776-- @param email E-mail Address of the account to log in.
777-- @param password Password of the account to log in.
778-- (WARNING: Do NOT store this password in a GitHub repo or anything of the sorts.)
779-- @return True or False depending on success.
780function Client:login(email, password)
781
782 local payload = {
783 email = email,
784 password = password
785 }
786
787 local response = send(endpoints.login, 'POST', payload, self.headers)
788 local success = responseIsSuccessful(response)
789
790 if success then
791
792 self.token = decode(response.body).token
793 self.isLoggedIn = true
794 self.headers['authorization'] = self.token
795 self.email = email
796
797 self.user = self:getCurrentUser()
798
799 end
800
801 return self.token
802
803end
804
805function Client:loginWithToken(token)
806
807 self.token = token
808 self.isLoggedIn = true
809 self.headers['authorization'] = token
810
811 self.user = self:getCurrentUser()
812
813 return token
814end
815
816--- Logs the Client out of the Discord server.
817--- (This is currently useless/does nothing)
818-- @param email E-mail Address of the account to log in.
819-- @param password Password of the account to log in.
820-- (WARNING: Do NOT store this password in a GitHub repo or anything of the sorts.)
821-- @return True or False depending on success.
822function Client:logout()
823
824 if self.isLoggedIn then
825
826 local payload = {
827 token = self.token
828 }
829
830 local response = send(endpoints.login, 'POST', payload)
831 local success = responseIsSuccessful(response)
832
833 if success then
834 self.isLoggedIn = false
835 self.token = nil
836 end
837
838 return success
839
840 else
841 return false
842 end
843
844end
845
846--- Gets the current authentication token.
847--- (Only if logged in of course.)
848-- @return Authentication Token
849function Client:getToken()
850
851 if self.isLoggedIn then
852 return self.token
853 else
854 return nil
855 end
856
857end
858
859--- Gets the current Gateway we are connected to.
860--- (Only if logged in of course.)
861-- @return Gateway URL
862function Client:getGateway()
863
864 if self.isLoggedIn then
865
866 local response = send(endpoints.gateway, 'GET', nil, self.headers)
867
868 if responseIsSuccessful(response) then
869 return decode(response.body).url
870 else
871 return nil
872 end
873
874 end
875
876end
877
878--- Gets the current User information.
879--- (Only if logged in of course.)
880-- @return User Table
881function Client:getCurrentUser()
882
883 if self.isLoggedIn then
884
885 local response = send(endpoints.users .. '/@me', 'GET', nil, self.headers)
886 if responseIsSuccessful(response) then
887 return decode(response.body)
888 else
889 return nil
890 end
891
892 else
893 return nil
894 end
895
896end
897
898--- Gets the GuildMember information for userid in guild.
899-- @param guild The guild of the member.
900-- @param userid The id of the user.
901-- @return GuildMember Table
902function Client:getGuildMember(guild, userid)
903 if self.isLoggedIn then
904 local response = send(endpoints.servers .. "/" .. guild .. "/members/" .. userid, 'GET', nil, self.headers)
905 if responseIsSuccessful(response) then
906 return decode(response.body)
907 else
908 return nil
909 end
910 else
911 return nil
912 end
913end
914
915--- Gets roles for a guild.
916-- @param guild The guild to find roles.
917-- @return Table of roles
918function Client:getRoles(guild)
919 if self.isLoggedIn then
920 local response = send(endpoints.servers .. "/" .. guild .. "/roles", 'GET', nil, self.headers)
921 if responseIsSuccessful(response) then
922 return decode(response.body)
923 else
924 return nil
925 end
926 else
927 return nil
928 end
929end
930
931--- Gets a table of Servers the current User is connected to.
932--- (Only if logged in of course.)
933-- @return Server Table
934function Client:getServerList()
935
936 if self.isLoggedIn then
937
938 local response = send(endpoints.users .. '/@me/guilds', 'GET', nil, self.headers)
939
940 if responseIsSuccessful(response) then
941 self.user = decode(response.body)
942 return decode(response.body)
943 else
944 return nil
945 end
946
947 else
948 return nil
949 end
950
951end
952
953--- Gets a table of Channels from the provided Server id.
954--- (Only if logged in of course.)
955-- @param id Server ID
956-- @return Channel Table
957function Client:getChannelList(id)
958
959 if self.isLoggedIn then
960
961 local response = send(endpoints.servers .. '/' .. id .. '/channels', 'GET', nil, self.headers)
962
963 if responseIsSuccessful(response) then
964 return decode(response.body)
965 else
966 return nil
967 end
968
969 else
970 return nil
971 end
972
973end
974
975-- Gets all messages in a channel
976-- @param id Channel ID
977-- @return Array of messages (NOT Lua Message objects, but Discord internal)
978function Client:getMessages(id)
979 if self.isLoggedIn then
980 local response = http.get(endpoints.channels .. "/" .. id .. "/messages", self.headers)
981 if response.getResponseCode() >= 200 and response.getResponseCode() < 300 then
982 local res = decode(response.readAll())
983 response.close()
984 return res
985 else
986 return nil
987 end
988 else
989 return nil
990 end
991end
992
993--- Sends a Message from the Client to a Channel.
994--- (Only if logged in of course.)
995-- @param message Message to be sent.
996-- @param id ID for Channel to send to.
997-- @return Message Class
998function Client:sendMessage(message, id)
999
1000 if self.isLoggedIn then
1001
1002 local payload = {
1003 content = tostring(message)
1004 }
1005
1006 local response = http.post(endpoints.channels .. '/' .. id .. '/messages', textutils.serializeJSON(payload), self.headers)
1007
1008 --return :new(decode(response.body), self.token)
1009 --local ret = decode(response.readAll())
1010 --response.close()
1011 return nil
1012
1013 else
1014 return nil
1015 end
1016
1017end
1018
1019--- Edits a sent Message.
1020--- (Only if logged in of course.)
1021-- @param message The new message to replace the old one.
1022-- @param msgClass Message Class to edit.
1023-- @return Message Class
1024function Client:editMessage(message, msgClass)
1025
1026 if self.isLoggedIn then
1027
1028 msgClass:edit(message)
1029
1030 end
1031
1032end
1033
1034--- Deletes a sent Message.
1035--- (Only if logged in of course.)
1036-- @param msgClass Message Class to delete.
1037-- @return Message Class
1038function Client:deleteMessage(msgClass)
1039
1040 if self.isLoggedIn then
1041
1042 msgClass:delete()
1043
1044 end
1045
1046end
1047
1048--- Starts the Client loop.
1049--- (Does nothing of real use at the moment, will interact with WebSockets in the future)
1050function Client:run()
1051
1052 if self.isLoggedIn then
1053
1054 while true do
1055 if self.callbacks.think then self.callbacks.think() end
1056
1057 end
1058
1059 end
1060
1061end