Initial commit
This commit is contained in:
parent
9329e09d5f
commit
dfbeb1c041
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
### macOS ###
|
||||
*.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
auth.txt
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
.vs/slnx.sqlite
|
||||
.vs
|
58
README.md
58
README.md
@ -1,2 +1,56 @@
|
||||
# discord
|
||||
A Minetest Discord bridge with flexibility.
|
||||
# Discord for Minetest
|
||||
____
|
||||
######_Part of the SolarSail family._
|
||||
### Notice:
|
||||
|
||||
This mod requires LuaJIT; attempts at running it as a standard Lua interpreter will result in Minetest failing to execute this mod.
|
||||
|
||||
### Preparation:
|
||||
|
||||
`git clone` or manually download a zip of this folder and extract it to `minetest/mods` resulting in `minetest/mods/discord`.
|
||||
|
||||
Install cURL. You can find this in your package manager, in the case of Windows, this is provided for you.
|
||||
|
||||
### Golang Bot Setup Procedure:
|
||||
|
||||
Install the latest version of [Golang](https://golang.org/).
|
||||
|
||||
Using the terminal, execute `go get github.com/bwmarrin/discordgo`
|
||||
|
||||
Afterwards, you can test that the Golang is installed and functioning correctly for your environment by running `go run main.go` inside the `discord` folder.
|
||||
|
||||
By default, the bot will complain of a missing `auth.txt` and generate an empty file for you.
|
||||
|
||||
The error, will always come out as this in the terminal:
|
||||
|
||||
```
|
||||
$ go run main.go
|
||||
Failed to load auth.txt, a file named auth.txt has been created.
|
||||
The file should contain the following lines:
|
||||
|
||||
<Discord bot token>
|
||||
<Discord text channel ID>
|
||||
<Discord server ID>
|
||||
```
|
||||
|
||||
The auth.txt contents should look like this example, but not exactly as the token, server ID and channel ID can differ dramatically:
|
||||
|
||||
```
|
||||
abcdefghijklmop1234.5
|
||||
1234567890987654321
|
||||
0987654321234567890
|
||||
```
|
||||
|
||||
### Minetest Mod Setup Procedure
|
||||
|
||||
Add this mod to the chosen world via `worldmods` or by including it from the `mods` directory.
|
||||
|
||||
Grant this mod insecure environment access via `security.trusted_mods = discord`.
|
||||
|
||||
### Licensing:
|
||||
|
||||
This mod (`main.go` and `init.lua`) is licensed as MIT.
|
||||
|
||||
Discord.lua is licensed as MIT and can be found here: https://github.com/videah/discord.lua
|
||||
|
||||
`luajit-request`'s license can be found at `discord/discord/luajit-request/LICENSE`
|
BIN
bin/curl.exe
Normal file
BIN
bin/curl.exe
Normal file
Binary file not shown.
BIN
bin/libcurl.exe
Normal file
BIN
bin/libcurl.exe
Normal file
Binary file not shown.
182
discord/class.lua
Normal file
182
discord/class.lua
Normal file
@ -0,0 +1,182 @@
|
||||
local middleclass = {
|
||||
_VERSION = 'middleclass v3.1.0',
|
||||
_DESCRIPTION = 'Object Orientation for Lua',
|
||||
_URL = 'https://github.com/kikito/middleclass',
|
||||
_LICENSE = [[
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2011 Enrique García Cota
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
}
|
||||
|
||||
local function _setClassDictionariesMetatables(aClass)
|
||||
local dict = aClass.__instanceDict
|
||||
dict.__index = dict
|
||||
|
||||
local super = aClass.super
|
||||
if super then
|
||||
local superStatic = super.static
|
||||
setmetatable(dict, super.__instanceDict)
|
||||
setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) or superStatic[k] end })
|
||||
else
|
||||
setmetatable(aClass.static, { __index = function(_,k) return dict[k] end })
|
||||
end
|
||||
end
|
||||
|
||||
local function _setClassMetatable(aClass)
|
||||
setmetatable(aClass, {
|
||||
__tostring = function() return "class " .. aClass.name end,
|
||||
__index = aClass.static,
|
||||
__newindex = aClass.__instanceDict,
|
||||
__call = function(self, ...) return self:new(...) end
|
||||
})
|
||||
end
|
||||
|
||||
local function _createClass(name, super)
|
||||
local aClass = { name = name, super = super, static = {}, __mixins = {}, __instanceDict={} }
|
||||
aClass.subclasses = setmetatable({}, {__mode = "k"})
|
||||
|
||||
_setClassDictionariesMetatables(aClass)
|
||||
_setClassMetatable(aClass)
|
||||
|
||||
return aClass
|
||||
end
|
||||
|
||||
local function _createLookupMetamethod(aClass, name)
|
||||
return function(...)
|
||||
local method = aClass.super[name]
|
||||
assert( type(method)=='function', tostring(aClass) .. " doesn't implement metamethod '" .. name .. "'" )
|
||||
return method(...)
|
||||
end
|
||||
end
|
||||
|
||||
local function _setClassMetamethods(aClass)
|
||||
for _,m in ipairs(aClass.__metamethods) do
|
||||
aClass[m]= _createLookupMetamethod(aClass, m)
|
||||
end
|
||||
end
|
||||
|
||||
local function _setDefaultInitializeMethod(aClass, super)
|
||||
aClass.initialize = function(instance, ...)
|
||||
return super.initialize(instance, ...)
|
||||
end
|
||||
end
|
||||
|
||||
local function _includeMixin(aClass, mixin)
|
||||
assert(type(mixin)=='table', "mixin must be a table")
|
||||
for name,method in pairs(mixin) do
|
||||
if name ~= "included" and name ~= "static" then aClass[name] = method end
|
||||
end
|
||||
if mixin.static then
|
||||
for name,method in pairs(mixin.static) do
|
||||
aClass.static[name] = method
|
||||
end
|
||||
end
|
||||
if type(mixin.included)=="function" then mixin:included(aClass) end
|
||||
aClass.__mixins[mixin] = true
|
||||
end
|
||||
|
||||
local Object = _createClass("Object", nil)
|
||||
|
||||
Object.static.__metamethods = { '__add', '__band', '__bor', '__bxor', '__bnot', '__call', '__concat',
|
||||
'__div', '__eq', '__ipairs', '__idiv', '__le', '__len', '__lt', '__mod',
|
||||
'__mul', '__pairs', '__pow', '__shl', '__shr', '__sub', '__tostring', '__unm' }
|
||||
|
||||
function Object.static:allocate()
|
||||
assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'")
|
||||
return setmetatable({ class = self }, self.__instanceDict)
|
||||
end
|
||||
|
||||
function Object.static:new(...)
|
||||
local instance = self:allocate()
|
||||
instance:initialize(...)
|
||||
return instance
|
||||
end
|
||||
|
||||
function Object.static:subclass(name)
|
||||
assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'")
|
||||
assert(type(name) == "string", "You must provide a name(string) for your class")
|
||||
|
||||
local subclass = _createClass(name, self)
|
||||
_setClassMetamethods(subclass)
|
||||
_setDefaultInitializeMethod(subclass, self)
|
||||
self.subclasses[subclass] = true
|
||||
self:subclassed(subclass)
|
||||
|
||||
return subclass
|
||||
end
|
||||
|
||||
function Object.static:subclassed(other) end
|
||||
|
||||
function Object.static:isSubclassOf(other)
|
||||
return type(other) == 'table' and
|
||||
type(self) == 'table' and
|
||||
type(self.super) == 'table' and
|
||||
( self.super == other or
|
||||
type(self.super.isSubclassOf) == 'function' and
|
||||
self.super:isSubclassOf(other)
|
||||
)
|
||||
end
|
||||
|
||||
function Object.static:include( ... )
|
||||
assert(type(self) == 'table', "Make sure you that you are using 'Class:include' instead of 'Class.include'")
|
||||
for _,mixin in ipairs({...}) do _includeMixin(self, mixin) end
|
||||
return self
|
||||
end
|
||||
|
||||
function Object.static:includes(mixin)
|
||||
return type(mixin) == 'table' and
|
||||
type(self) == 'table' and
|
||||
type(self.__mixins) == 'table' and
|
||||
( self.__mixins[mixin] or
|
||||
type(self.super) == 'table' and
|
||||
type(self.super.includes) == 'function' and
|
||||
self.super:includes(mixin)
|
||||
)
|
||||
end
|
||||
|
||||
function Object:initialize() end
|
||||
|
||||
function Object:__tostring() return "instance of " .. tostring(self.class) end
|
||||
|
||||
function Object:isInstanceOf(aClass)
|
||||
return type(self) == 'table' and
|
||||
type(self.class) == 'table' and
|
||||
type(aClass) == 'table' and
|
||||
( aClass == self.class or
|
||||
type(aClass.isSubclassOf) == 'function' and
|
||||
self.class:isSubclassOf(aClass)
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
|
||||
function middleclass.class(name, super, ...)
|
||||
super = super or Object
|
||||
return super:subclass(name, ...)
|
||||
end
|
||||
|
||||
middleclass.Object = Object
|
||||
|
||||
setmetatable(middleclass, { __call = function(_, ...) return middleclass.class(...) end })
|
||||
|
||||
return middleclass
|
299
discord/client.lua
Normal file
299
discord/client.lua
Normal file
@ -0,0 +1,299 @@
|
||||
-- This code is licensed under the MIT Open Source License.
|
||||
|
||||
-- Copyright (c) 2015 Ruairidh Carmichael - ruairidhcarmichael@live.co.uk
|
||||
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
-- of this software and associated documentation files (the "Software"), to deal
|
||||
-- in the Software without restriction, including without limitation the rights
|
||||
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
-- copies of the Software, and to permit persons to whom the Software is
|
||||
-- furnished to do so, subject to the following conditions:
|
||||
|
||||
-- The above copyright notice and this permission notice shall be included in
|
||||
-- all copies or substantial portions of the Software.
|
||||
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
-- THE SOFTWARE.
|
||||
|
||||
-------------------------------------
|
||||
-- Discord Client Class
|
||||
-- @module Client
|
||||
|
||||
local path = (...):match('(.-)[^%.]+$')
|
||||
|
||||
local request = require(path .. 'wrapper')
|
||||
|
||||
local class = require(path .. 'class')
|
||||
local json = require(path .. 'json')
|
||||
local endpoints = require(path .. 'endpoints')
|
||||
local util = require(path .. 'utils')
|
||||
|
||||
local Message = require(path .. 'message')
|
||||
|
||||
print('Loaded client')
|
||||
|
||||
local Client = class('ClientObject')
|
||||
|
||||
--- Internally initialize's the Client class.
|
||||
--- Please use Client:new(options) instead.
|
||||
-- @param options Options table (Currently useless.)
|
||||
function Client:initialize(options)
|
||||
|
||||
self.isLoggedIn = false
|
||||
self.options = options
|
||||
self.token = ''
|
||||
self.email = ''
|
||||
self.user = {}
|
||||
|
||||
self.headers = {}
|
||||
|
||||
self.headers['authorization'] = self.token
|
||||
self.headers['Content-Type'] = 'application/json'
|
||||
|
||||
self.callbacks = {}
|
||||
|
||||
self.socket = nil
|
||||
|
||||
end
|
||||
|
||||
--- Logs the Client into Discord's servers.
|
||||
--- This is required to use most of the Clients functions.
|
||||
-- @param email E-mail Address of the account to log in.
|
||||
-- @param password Password of the account to log in.
|
||||
-- (WARNING: Do NOT store this password in a GitHub repo or anything of the sorts.)
|
||||
-- @return True or False depending on success.
|
||||
function Client:login(email, password)
|
||||
|
||||
local payload = {
|
||||
email = email,
|
||||
password = password
|
||||
}
|
||||
|
||||
local response = request.send(endpoints.login, 'POST', payload, self.headers)
|
||||
local success = util.responseIsSuccessful(response)
|
||||
|
||||
if success then
|
||||
|
||||
self.token = json.decode(response.body).token
|
||||
self.isLoggedIn = true
|
||||
self.headers['authorization'] = self.token
|
||||
self.email = email
|
||||
|
||||
self.user = self:getCurrentUser()
|
||||
|
||||
end
|
||||
|
||||
return self.token
|
||||
|
||||
end
|
||||
|
||||
function Client:loginWithToken(token)
|
||||
|
||||
self.token = token
|
||||
self.isLoggedIn = true
|
||||
self.headers['authorization'] = "Bot "..token
|
||||
|
||||
self.user = self:getCurrentUser()
|
||||
|
||||
return token
|
||||
end
|
||||
|
||||
--- Logs the Client out of the Discord server.
|
||||
--- (This is currently useless/does nothing)
|
||||
-- @param email E-mail Address of the account to log in.
|
||||
-- @param password Password of the account to log in.
|
||||
-- (WARNING: Do NOT store this password in a GitHub repo or anything of the sorts.)
|
||||
-- @return True or False depending on success.
|
||||
function Client:logout()
|
||||
|
||||
if self.isLoggedIn then
|
||||
|
||||
local payload = {
|
||||
token = self.token
|
||||
}
|
||||
|
||||
local response = request.send(endpoints.login, 'POST', payload)
|
||||
local success = util.responseIsSuccessful(response)
|
||||
|
||||
if success then
|
||||
self.isLoggedIn = false
|
||||
self.token = nil
|
||||
end
|
||||
|
||||
return success
|
||||
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Gets the current authentication token.
|
||||
--- (Only if logged in of course.)
|
||||
-- @return Authentication Token
|
||||
function Client:getToken()
|
||||
|
||||
if self.isLoggedIn then
|
||||
return self.token
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Gets the current Gateway we are connected to.
|
||||
--- (Only if logged in of course.)
|
||||
-- @return Gateway URL
|
||||
function Client:getGateway()
|
||||
|
||||
if self.isLoggedIn then
|
||||
|
||||
local response = request.send(endpoints.gateway, 'GET', nil, self.headers)
|
||||
|
||||
if util.responseIsSuccessful(response) then
|
||||
return json.decode(response.body).url
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Gets the current User information.
|
||||
--- (Only if logged in of course.)
|
||||
-- @return User Table
|
||||
function Client:getCurrentUser()
|
||||
|
||||
if self.isLoggedIn then
|
||||
|
||||
local response = request.send(endpoints.users .. '/@me', 'GET', nil, self.headers)
|
||||
if util.responseIsSuccessful(response) then
|
||||
return json.decode(response.body)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Gets a table of Servers the current User is connected to.
|
||||
--- (Only if logged in of course.)
|
||||
-- @return Server Table
|
||||
function Client:getServerList()
|
||||
|
||||
if self.isLoggedIn then
|
||||
|
||||
local response = request.send(endpoints.users .. '/@me/guilds', 'GET', nil, self.headers)
|
||||
|
||||
if util.responseIsSuccessful(response) then
|
||||
self.user = json.decode(response.body)
|
||||
return json.decode(response.body)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Gets a table of Channels from the provided Server id.
|
||||
--- (Only if logged in of course.)
|
||||
-- @param id Server ID
|
||||
-- @return Channel Table
|
||||
function Client:getChannelList(id)
|
||||
|
||||
if self.isLoggedIn then
|
||||
|
||||
local response = request.send(endpoints.servers .. '/' .. id .. '/channels', 'GET', nil, self.headers)
|
||||
|
||||
if util.responseIsSuccessful(response) then
|
||||
return json.decode(response.body)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Sends a Message from the Client to a Channel.
|
||||
--- (Only if logged in of course.)
|
||||
-- @param message Message to be sent.
|
||||
-- @param id ID for Channel to send to.
|
||||
-- @return Message Class
|
||||
function Client:sendMessage(message, id)
|
||||
|
||||
if self.isLoggedIn then
|
||||
|
||||
local payload = {
|
||||
content = tostring(message)
|
||||
}
|
||||
|
||||
local response = request.send(endpoints.channels .. '/' .. id .. '/messages', 'POST', payload, self.headers)
|
||||
|
||||
return Message:new(json.decode(response.body), self.token)
|
||||
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Edits a sent Message.
|
||||
--- (Only if logged in of course.)
|
||||
-- @param message The new message to replace the old one.
|
||||
-- @param msgClass Message Class to edit.
|
||||
-- @return Message Class
|
||||
function Client:editMessage(message, msgClass)
|
||||
|
||||
if self.isLoggedIn then
|
||||
|
||||
msgClass:edit(message)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Deletes a sent Message.
|
||||
--- (Only if logged in of course.)
|
||||
-- @param msgClass Message Class to delete.
|
||||
-- @return Message Class
|
||||
function Client:deleteMessage(msgClass)
|
||||
|
||||
if self.isLoggedIn then
|
||||
|
||||
msgClass:delete()
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Starts the Client loop.
|
||||
--- (Does nothing of real use at the moment, will interact with WebSockets in the future)
|
||||
function Client:run()
|
||||
|
||||
if self.isLoggedIn then
|
||||
|
||||
while true do
|
||||
if self.callbacks.think then self.callbacks.think() end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return Client
|
35
discord/endpoints.lua
Normal file
35
discord/endpoints.lua
Normal file
@ -0,0 +1,35 @@
|
||||
-- This code is licensed under the MIT Open Source License.
|
||||
|
||||
-- Copyright (c) 2015 Ruairidh Carmichael - ruairidhcarmichael@live.co.uk
|
||||
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
-- of this software and associated documentation files (the "Software"), to deal
|
||||
-- in the Software without restriction, including without limitation the rights
|
||||
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
-- copies of the Software, and to permit persons to whom the Software is
|
||||
-- furnished to do so, subject to the following conditions:
|
||||
|
||||
-- The above copyright notice and this permission notice shall be included in
|
||||
-- all copies or substantial portions of the Software.
|
||||
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
-- THE SOFTWARE.
|
||||
|
||||
local endp = {}
|
||||
|
||||
endp.base = 'https://discordapp.com'
|
||||
endp.apiBase = endp.base .. '/api'
|
||||
endp.gateway = endp.apiBase .. '/gateway'
|
||||
endp.users = endp.apiBase .. '/users'
|
||||
endp.register = endp.apiBase .. '/auth/register'
|
||||
endp.login = endp.apiBase .. '/auth/login'
|
||||
endp.logout = endp.apiBase .. '/auth/logout'
|
||||
endp.servers = endp.apiBase .. '/guilds'
|
||||
endp.channels = endp.apiBase .. '/channels'
|
||||
|
||||
return endp
|
35
discord/init.lua
Normal file
35
discord/init.lua
Normal file
@ -0,0 +1,35 @@
|
||||
-- This code is licensed under the MIT Open Source License.
|
||||
|
||||
-- Copyright (c) 2015 Ruairidh Carmichael - ruairidhcarmichael@live.co.uk
|
||||
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
-- of this software and associated documentation files (the "Software"), to deal
|
||||
-- in the Software without restriction, including without limitation the rights
|
||||
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
-- copies of the Software, and to permit persons to whom the Software is
|
||||
-- furnished to do so, subject to the following conditions:
|
||||
|
||||
-- The above copyright notice and this permission notice shall be included in
|
||||
-- all copies or substantial portions of the Software.
|
||||
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
-- THE SOFTWARE.
|
||||
|
||||
local path = (...):match('(.-)[^%.]+$')
|
||||
|
||||
local discord = {
|
||||
|
||||
_VERSION = '0.0.1',
|
||||
_DESCRIPTION = 'Lua API Wrapper for Discord'
|
||||
|
||||
}
|
||||
|
||||
discord.Client = require(path .. 'client')
|
||||
discord.Message = require(path .. 'message')
|
||||
|
||||
return discord
|
375
discord/json.lua
Normal file
375
discord/json.lua
Normal file
@ -0,0 +1,375 @@
|
||||
--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2015 rxi
|
||||
--
|
||||
-- This library is free software; you can redistribute it and/or modify it
|
||||
-- under the terms of the MIT license. See LICENSE for details.
|
||||
--
|
||||
|
||||
local json = { _version = "0.1.0" }
|
||||
|
||||
-- Encode
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\\\",
|
||||
[ "\"" ] = "\\\"",
|
||||
[ "\b" ] = "\\b",
|
||||
[ "\f" ] = "\\f",
|
||||
[ "\n" ] = "\\n",
|
||||
[ "\r" ] = "\\r",
|
||||
[ "\t" ] = "\\t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "\\/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return escape_char_map[c] or string.format("\\u%04x", c:byte())
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil(val)
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, stack)
|
||||
local res = {}
|
||||
stack = stack or {}
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if val[1] ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for i, v in ipairs(val) do
|
||||
table.insert(res, encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "[" .. table.concat(res, ",") .. "]"
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "{" .. table.concat(res, ",") .. "}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, stack)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, stack)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.encode(val)
|
||||
return ( encode(val) )
|
||||
end
|
||||
|
||||
-- Decode
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(3, 6), 16 )
|
||||
local n2 = tonumber( s:sub(9, 12), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local has_unicode_escape = false
|
||||
local has_surrogate_escape = false
|
||||
local has_escape = false
|
||||
local last
|
||||
for j = i + 1, #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
end
|
||||
|
||||
if last == 92 then -- "\\" (escape char)
|
||||
if x == 117 then -- "u" (unicode escape sequence)
|
||||
local hex = str:sub(j + 1, j + 5)
|
||||
if not hex:find("%x%x%x%x") then
|
||||
decode_error(str, j, "invalid unicode escape in string")
|
||||
end
|
||||
if hex:find("^[dD][89aAbB]") then
|
||||
has_surrogate_escape = true
|
||||
else
|
||||
has_unicode_escape = true
|
||||
end
|
||||
else
|
||||
local c = string.char(x)
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
has_escape = true
|
||||
end
|
||||
last = nil
|
||||
|
||||
elseif x == 34 then -- '"' (end of string)
|
||||
local s = str:sub(i + 1, j - 1)
|
||||
if has_surrogate_escape then
|
||||
s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
|
||||
end
|
||||
if has_unicode_escape then
|
||||
s = s:gsub("\\u....", parse_unicode_escape)
|
||||
end
|
||||
if has_escape then
|
||||
s = s:gsub("\\.", escape_char_map_inv)
|
||||
end
|
||||
return s, j + 1
|
||||
|
||||
else
|
||||
last = x
|
||||
end
|
||||
end
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
return ( parse(str, next_char(str, 1, space_chars, true)) )
|
||||
end
|
||||
|
||||
|
||||
return json
|
11
discord/luajit-request/LICENSE.txt
Normal file
11
discord/luajit-request/LICENSE.txt
Normal file
@ -0,0 +1,11 @@
|
||||
Copyright (c) 2014 Lucien Greathouse
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
363
discord/luajit-request/init.lua
Normal file
363
discord/luajit-request/init.lua
Normal file
@ -0,0 +1,363 @@
|
||||
--[[
|
||||
LuaJIT-Request
|
||||
Lucien Greathouse
|
||||
Wrapper for LuaJIT-cURL for easy HTTP(S) requests.
|
||||
|
||||
Copyright (c) 2016 Lucien Greathouse
|
||||
|
||||
This software is provided 'as-is', without any express
|
||||
or implied warranty. In no event will the authors be held
|
||||
liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, andto alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must
|
||||
not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
]]
|
||||
|
||||
local path = (...):gsub("%.init$", ""):match("%.?(.-)$") .. "."
|
||||
|
||||
local ffi = require("ffi")
|
||||
local curl = require(path .. "luajit-curl")
|
||||
local request
|
||||
|
||||
local function url_encode(str)
|
||||
if (str) then
|
||||
str = str:gsub("\n", "\r\n")
|
||||
str = str:gsub("([^%w %-%_%.%~])", function(c)
|
||||
return string.format ("%%%02X", string.byte(c))
|
||||
end)
|
||||
str = str:gsub(" ", "%%20")
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
local function cookie_encode(str, name)
|
||||
str = str:gsub("[,;%s]", "")
|
||||
|
||||
if (name) then
|
||||
str = str:gsub("=", "")
|
||||
end
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
local auth_map = {
|
||||
BASIC = ffi.cast("long", curl.CURLAUTH_BASIC),
|
||||
DIGEST = ffi.cast("long", curl.CURLAUTH_DIGEST),
|
||||
NEGOTIATE = ffi.cast("long", curl.CURLAUTH_NEGOTIATE)
|
||||
}
|
||||
|
||||
local errors = {
|
||||
unknown = 0,
|
||||
timeout = 1,
|
||||
connect = 2,
|
||||
resolve_host = 3
|
||||
}
|
||||
|
||||
local code_map = {
|
||||
[curl.CURLE_OPERATION_TIMEDOUT] = {
|
||||
errors.timeout, "Connection timed out"
|
||||
},
|
||||
[curl.CURLE_COULDNT_RESOLVE_HOST] = {
|
||||
errors.resolve_host, "Couldn't resolve host"
|
||||
},
|
||||
[curl.CURLE_COULDNT_CONNECT] = {
|
||||
errors.connect, "Couldn't connect to host"
|
||||
}
|
||||
}
|
||||
|
||||
request = {
|
||||
error = errors,
|
||||
|
||||
version = "2.4.0",
|
||||
version_major = 2,
|
||||
version_minor = 4,
|
||||
version_patch = 0,
|
||||
|
||||
--[[
|
||||
Send an HTTP(S) request to the URL at 'url' using the HTTP method 'method'.
|
||||
Use the 'args' parameter to optionally configure the request:
|
||||
- method: HTTP method to use. Defaults to "GET", but can be any HTTP verb like "POST" or "PUT"
|
||||
- headers: Dictionary of additional HTTP headers to send with request
|
||||
- data: Dictionary or string to send as request body
|
||||
- cookies: Dictionary table of cookies to send
|
||||
- timeout: How long to wait for the connection to be made before giving up
|
||||
- allow_redirects: Whether or not to allow redirection. Defaults to true
|
||||
- body_stream_callback: A method to call with each piece of the response body.
|
||||
- header_stream_callback: A method to call with each piece of the resulting header.
|
||||
- transfer_info_callback: A method to call with transfer progress data.
|
||||
- auth_type: Authentication method to use. Defaults to "none", but can also be "basic", "digest" or "negotiate"
|
||||
- username: A username to use with authentication. 'auth_type' must also be specified.
|
||||
- password: A password to use with authentication. 'auth_type' must also be specified.
|
||||
- files: A dictionary of file names to their paths on disk to upload via stream.
|
||||
|
||||
If both body_stream_callback and header_stream_callback are defined, a boolean true will be returned instead of the following object.
|
||||
|
||||
The return object is a dictionary with the following members:
|
||||
- code: The HTTP status code the response gave. Will not exist if header_stream_callback is defined above.
|
||||
- body: The body of the response. Will not exist if body_stream_callback is defined above.
|
||||
- headers: A dictionary of headers and their values. Will not exist if header_stream_callback is defined above.
|
||||
- headers_raw: A raw string containing the actual headers the server sent back. Will not exist if header_stream_callback is defined above.
|
||||
- set_cookies: A dictionary of cookies given by the "Set-Cookie" header from the server. Will not exist if the server did not set any cookies.
|
||||
|
||||
]]
|
||||
send = function(url, args)
|
||||
local handle = curl.curl_easy_init()
|
||||
local header_chunk
|
||||
local out_buffer
|
||||
local headers_buffer
|
||||
args = args or {}
|
||||
|
||||
local callbacks = {}
|
||||
local gc_handles = {}
|
||||
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_URL, url)
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_SSL_VERIFYPEER, 1)
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_SSL_VERIFYHOST, 2)
|
||||
|
||||
if (args.method) then
|
||||
local method = string.upper(tostring(args.method))
|
||||
|
||||
if (method == "GET") then
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_HTTPGET, 1)
|
||||
elseif (method == "POST") then
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_POST, 1)
|
||||
else
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_CUSTOMREQUEST, method)
|
||||
end
|
||||
end
|
||||
|
||||
if (args.headers) then
|
||||
for key, value in pairs(args.headers) do
|
||||
header_chunk = curl.curl_slist_append(header_chunk, tostring(key) .. ":" .. tostring(value))
|
||||
end
|
||||
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_HTTPHEADER, header_chunk)
|
||||
end
|
||||
|
||||
if (args.auth_type) then
|
||||
local auth = string.upper(tostring(args.auth_type))
|
||||
|
||||
if (auth_map[auth]) then
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_HTTPAUTH, auth_map[auth])
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_USERNAME, tostring(args.username))
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_PASSWORD, tostring(args.password or ""))
|
||||
elseif (auth ~= "NONE") then
|
||||
error("Unsupported authentication type '" .. auth .. "'")
|
||||
end
|
||||
end
|
||||
|
||||
if (args.body_stream_callback) then
|
||||
local callback = ffi.cast("curl_callback", function(data, size, nmeb, user)
|
||||
args.body_stream_callback(ffi.string(data, size * nmeb))
|
||||
return size * nmeb
|
||||
end)
|
||||
|
||||
table.insert(callbacks, callback)
|
||||
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_WRITEFUNCTION, callback)
|
||||
else
|
||||
out_buffer = {}
|
||||
|
||||
local callback = ffi.cast("curl_callback", function(data, size, nmeb, user)
|
||||
table.insert(out_buffer, ffi.string(data, size * nmeb))
|
||||
return size * nmeb
|
||||
end)
|
||||
|
||||
table.insert(callbacks, callback)
|
||||
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_WRITEFUNCTION, callback)
|
||||
end
|
||||
|
||||
if (args.header_stream_callback) then
|
||||
local callback = ffi.cast("curl_callback", function(data, size, nmeb, user)
|
||||
args.header_stream_callback(ffi.string(data, size * nmeb))
|
||||
return size * nmeb
|
||||
end)
|
||||
|
||||
table.insert(callbacks, callback)
|
||||
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_HEADERFUNCTION, callback)
|
||||
else
|
||||
headers_buffer = {}
|
||||
|
||||
local callback = ffi.cast("curl_callback", function(data, size, nmeb, user)
|
||||
table.insert(headers_buffer, ffi.string(data, size * nmeb))
|
||||
return size * nmeb
|
||||
end)
|
||||
|
||||
table.insert(callbacks, callback)
|
||||
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_HEADERFUNCTION, callback)
|
||||
end
|
||||
|
||||
if (args.transfer_info_callback) then
|
||||
local callback = ffi.cast("curl_xferinfo_callback", function(client, dltotal, dlnow, ultotal, ulnow)
|
||||
args.transfer_info_callback(tonumber(dltotal), tonumber(dlnow), tonumber(ultotal), tonumber(ulnow))
|
||||
return 0
|
||||
end)
|
||||
|
||||
table.insert(callbacks, callback)
|
||||
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_NOPROGRESS, 0)
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_XFERINFOFUNCTION, callback)
|
||||
end
|
||||
|
||||
if (args.follow_redirects == nil) then
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_FOLLOWLOCATION, true)
|
||||
else
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_FOLLOWLOCATION, not not args.follow_redirects)
|
||||
end
|
||||
|
||||
if (args.data) then
|
||||
if (type(args.data) == "table") then
|
||||
local buffer = {}
|
||||
for key, value in pairs(args.data) do
|
||||
table.insert(buffer, ("%s=%s"):format(url_encode(key), url_encode(value)))
|
||||
end
|
||||
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_POSTFIELDS, table.concat(buffer, "&"))
|
||||
else
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_POSTFIELDS, tostring(args.data))
|
||||
end
|
||||
end
|
||||
|
||||
local post
|
||||
if (args.files) then
|
||||
post = ffi.new("struct curl_httppost*[1]")
|
||||
local lastptr = ffi.new("struct curl_httppost*[1]")
|
||||
|
||||
for key, value in pairs(args.files) do
|
||||
local file = ffi.new("char[?]", #value, value)
|
||||
|
||||
table.insert(gc_handles, file)
|
||||
|
||||
local res = curl.curl_formadd(
|
||||
post, lastptr,
|
||||
ffi.new("int", curl.CURLFORM_COPYNAME), key,
|
||||
ffi.new("int", curl.CURLFORM_FILE), file,
|
||||
ffi.new("int", curl.CURLFORM_END)
|
||||
)
|
||||
end
|
||||
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_HTTPPOST, post[0])
|
||||
end
|
||||
|
||||
-- Enable the cookie engine
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_COOKIEFILE, "")
|
||||
|
||||
if (args.cookies) then
|
||||
local cookie_out
|
||||
|
||||
if (type(args.cookies) == "table") then
|
||||
local buffer = {}
|
||||
for key, value in pairs(args.cookies) do
|
||||
table.insert(buffer, ("%s=%s"):format(cookie_encode(key, true), cookie_encode(value)))
|
||||
end
|
||||
|
||||
cookie_out = table.concat(buffer, "; ")
|
||||
else
|
||||
cookie_out = tostring(args.cookies)
|
||||
end
|
||||
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_COOKIE, cookie_out)
|
||||
end
|
||||
|
||||
if (tonumber(args.timeout)) then
|
||||
curl.curl_easy_setopt(handle, curl.CURLOPT_CONNECTTIMEOUT, tonumber(args.timeout))
|
||||
end
|
||||
|
||||
local code = curl.curl_easy_perform(handle)
|
||||
|
||||
if (code ~= curl.CURLE_OK) then
|
||||
local num = tonumber(code)
|
||||
|
||||
if (code_map[num]) then
|
||||
return false, code_map[num][1], code_map[num][2]
|
||||
end
|
||||
|
||||
return false, request.error.unknown, "Unknown error", num
|
||||
end
|
||||
|
||||
local out
|
||||
|
||||
if (out_buffer or headers_buffer) then
|
||||
local headers, status, parsed_headers, raw_cookies, set_cookies
|
||||
|
||||
if (headers_buffer) then
|
||||
headers = table.concat(headers_buffer)
|
||||
status = headers:match("%s+(%d+)%s+")
|
||||
|
||||
parsed_headers = {}
|
||||
|
||||
for key, value in headers:gmatch("\n([^:]+): *([^\r\n]*)") do
|
||||
parsed_headers[key] = value
|
||||
end
|
||||
end
|
||||
|
||||
local cookielist = ffi.new("struct curl_slist*[1]")
|
||||
curl.curl_easy_getinfo(handle, curl.CURLINFO_COOKIELIST, cookielist)
|
||||
if cookielist[0] ~= nil then
|
||||
raw_cookies, set_cookies = {}, {}
|
||||
local cookielist = ffi.gc(cookielist[0], curl.curl_slist_free_all)
|
||||
local cookie = cookielist
|
||||
|
||||
repeat
|
||||
local raw = ffi.string(cookie[0].data)
|
||||
table.insert(raw_cookies, raw)
|
||||
|
||||
local domain, subdomains, path, secure, expiration, name, value = raw:match("^(.-)\t(.-)\t(.-)\t(.-)\t(.-)\t(.-)\t(.*)$")
|
||||
set_cookies[name] = value
|
||||
cookie = cookie[0].next
|
||||
until cookie == nil
|
||||
end
|
||||
|
||||
out = {
|
||||
body = table.concat(out_buffer),
|
||||
headers = parsed_headers,
|
||||
raw_cookies = raw_cookies,
|
||||
set_cookies = set_cookies,
|
||||
code = status,
|
||||
raw_headers = headers
|
||||
}
|
||||
else
|
||||
out = true
|
||||
end
|
||||
|
||||
curl.curl_easy_cleanup(handle)
|
||||
curl.curl_slist_free_all(header_chunk)
|
||||
|
||||
if (post) then
|
||||
curl.curl_formfree(post[0])
|
||||
end
|
||||
gc_handles = {}
|
||||
|
||||
for i, v in ipairs(callbacks) do
|
||||
v:free()
|
||||
end
|
||||
|
||||
return out
|
||||
end,
|
||||
|
||||
init = function()
|
||||
curl.curl_global_init(curl.CURL_GLOBAL_ALL)
|
||||
end,
|
||||
|
||||
close = function()
|
||||
curl.curl_global_cleanup()
|
||||
end
|
||||
}
|
||||
|
||||
request.init()
|
||||
|
||||
return request
|
1016
discord/luajit-request/luajit-curl.lua
Normal file
1016
discord/luajit-request/luajit-curl.lua
Normal file
File diff suppressed because it is too large
Load Diff
86
discord/message.lua
Normal file
86
discord/message.lua
Normal file
@ -0,0 +1,86 @@
|
||||
-- This code is licensed under the MIT Open Source License.
|
||||
|
||||
-- Copyright (c) 2015 Ruairidh Carmichael - ruairidhcarmichael@live.co.uk
|
||||
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
-- of this software and associated documentation files (the "Software"), to deal
|
||||
-- in the Software without restriction, including without limitation the rights
|
||||
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
-- copies of the Software, and to permit persons to whom the Software is
|
||||
-- furnished to do so, subject to the following conditions:
|
||||
|
||||
-- The above copyright notice and this permission notice shall be included in
|
||||
-- all copies or substantial portions of the Software.
|
||||
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
-- THE SOFTWARE.
|
||||
|
||||
-------------------------------------
|
||||
-- Discord Message Class
|
||||
-- @module Message
|
||||
|
||||
local path = (...):match('(.-)[^%.]+$')
|
||||
|
||||
local request = require(path .. 'wrapper')
|
||||
|
||||
local class = require(path .. 'class')
|
||||
local json = require(path .. 'json')
|
||||
local endpoints = require(path .. 'endpoints')
|
||||
local util = require(path .. 'utils')
|
||||
|
||||
local Message = class('MessageObject')
|
||||
|
||||
--- Internally initialize's the Message class.
|
||||
--- Please use Message:new(packet, token) instead.
|
||||
-- @param packet Packet to be sent as the message.
|
||||
-- @param token Authentication token from login.
|
||||
function Message:initialize(packet, token)
|
||||
|
||||
self.packet = packet or {}
|
||||
self.token = token or ''
|
||||
|
||||
self.headers = {}
|
||||
self.headers['authorization'] = self.token
|
||||
self.headers['Content-Type'] = 'application/json'
|
||||
|
||||
end
|
||||
|
||||
--- Edits a sent message.
|
||||
-- @param msg The new message to replace the old one.
|
||||
-- @return Response Packet received from Discord
|
||||
function Message:edit(msg)
|
||||
|
||||
local payload = {
|
||||
content = tostring(msg)
|
||||
}
|
||||
|
||||
local response = request.send(endpoints.channels .. '/' .. self.packet.channel_id .. '/messages/' .. self.packet.id, 'PATCH', payload, self.headers)
|
||||
|
||||
if util.responseIsSuccessful(response) then
|
||||
self.packet = json.decode(response.body)
|
||||
end
|
||||
|
||||
return util.responseIsSuccessful(response)
|
||||
|
||||
end
|
||||
|
||||
--- Deletes a sent message.
|
||||
-- @return Response Packet received from Discord
|
||||
function Message:delete()
|
||||
|
||||
local response = request.send(endpoints.channels .. '/' .. self.packet.channel_id .. '/messages/' .. self.packet.id, 'DELETE', nil, self.headers)
|
||||
|
||||
if util.responseIsSuccessful(response) then
|
||||
self = nil
|
||||
end
|
||||
|
||||
return util.responseIsSuccessful(response)
|
||||
|
||||
end
|
||||
|
||||
return Message
|
36
discord/utils.lua
Normal file
36
discord/utils.lua
Normal file
@ -0,0 +1,36 @@
|
||||
-- This code is licensed under the MIT Open Source License.
|
||||
|
||||
-- Copyright (c) 2015 Ruairidh Carmichael - ruairidhcarmichael@live.co.uk
|
||||
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
-- of this software and associated documentation files (the "Software"), to deal
|
||||
-- in the Software without restriction, including without limitation the rights
|
||||
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
-- copies of the Software, and to permit persons to whom the Software is
|
||||
-- furnished to do so, subject to the following conditions:
|
||||
|
||||
-- The above copyright notice and this permission notice shall be included in
|
||||
-- all copies or substantial portions of the Software.
|
||||
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
-- THE SOFTWARE.
|
||||
|
||||
-------------------------------------
|
||||
-- Various Internal Utilitys
|
||||
-- @module Utils
|
||||
|
||||
local utils = {}
|
||||
|
||||
--- Returns true if the response was successful.
|
||||
-- @param response Response from Discord server.
|
||||
-- @return True or False depending on response
|
||||
function utils.responseIsSuccessful(response)
|
||||
return tonumber(response.code) >= 200 and tonumber(response.code) < 300
|
||||
end
|
||||
|
||||
return utils
|
42
discord/wrapper.lua
Normal file
42
discord/wrapper.lua
Normal file
@ -0,0 +1,42 @@
|
||||
-- This code is licensed under the MIT Open Source License.
|
||||
|
||||
-- Copyright (c) 2015 Ruairidh Carmichael - ruairidhcarmichael@live.co.uk
|
||||
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
-- of this software and associated documentation files (the "Software"), to deal
|
||||
-- in the Software without restriction, including without limitation the rights
|
||||
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
-- copies of the Software, and to permit persons to whom the Software is
|
||||
-- furnished to do so, subject to the following conditions:
|
||||
|
||||
-- The above copyright notice and this permission notice shall be included in
|
||||
-- all copies or substantial portions of the Software.
|
||||
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
-- THE SOFTWARE.
|
||||
|
||||
local path = (...):match('(.-)[^%.]+$')
|
||||
|
||||
local request = require(path .. 'luajit-request.init')
|
||||
local json = require(path .. 'json')
|
||||
|
||||
local requestWrapper = {}
|
||||
|
||||
function requestWrapper.send(endpoint, method, data, headers)
|
||||
|
||||
local payload = {}
|
||||
|
||||
if headers then payload['headers'] = headers end
|
||||
if method then payload['method'] = method end
|
||||
if data then payload['data'] = json.encode(data) end
|
||||
|
||||
return request.send(endpoint, payload)
|
||||
|
||||
end
|
||||
|
||||
return requestWrapper
|
BIN
dlls/libcurl.dll
Normal file
BIN
dlls/libcurl.dll
Normal file
Binary file not shown.
BIN
dlls/libeay32.dll
Normal file
BIN
dlls/libeay32.dll
Normal file
Binary file not shown.
BIN
dlls/libssh2.dll
Normal file
BIN
dlls/libssh2.dll
Normal file
Binary file not shown.
BIN
dlls/msvcr120.dll
Normal file
BIN
dlls/msvcr120.dll
Normal file
Binary file not shown.
BIN
dlls/ssleay32.dll
Normal file
BIN
dlls/ssleay32.dll
Normal file
Binary file not shown.
BIN
dlls/zlib1.dll
Normal file
BIN
dlls/zlib1.dll
Normal file
Binary file not shown.
110
init.lua
Normal file
110
init.lua
Normal file
@ -0,0 +1,110 @@
|
||||
-- prototype discord bridge for minetest
|
||||
local discord_token = ""
|
||||
local channel_id = ""
|
||||
|
||||
local line = 1
|
||||
|
||||
for lines in io.lines(minetest.get_modpath("discord") .. "/auth.txt") do
|
||||
if line == 1 then
|
||||
discord_token = tostring(lines)
|
||||
else
|
||||
channel_id = tostring(lines)
|
||||
break
|
||||
end
|
||||
line = line + 1
|
||||
end
|
||||
|
||||
local ie = minetest.request_insecure_environment()
|
||||
local old_require = require
|
||||
|
||||
require = ie.require
|
||||
|
||||
ie.package.path = package.path .. ";" .. minetest.get_modpath(minetest.get_current_modname()) .. "/?.lua"
|
||||
|
||||
local discord_c = require "discord.init"
|
||||
require = old_require
|
||||
|
||||
discord = {}
|
||||
mt_client = discord_c.Client:new()
|
||||
|
||||
discord.accepted_token = false
|
||||
if mt_client:loginWithToken(discord_token) then
|
||||
mt_client:sendMessage('[Server] Server started.', channel_id)
|
||||
discord.accepted_token = true
|
||||
end
|
||||
|
||||
function discord.send_message(message)
|
||||
if discord.accepted_token then
|
||||
mt_client:sendMessage(message, channel_id)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_on_chat_message(function(name, message)
|
||||
if discord.accepted_token then
|
||||
mt_client:sendMessage("<**" .. name .. "**> " .. minetest.strip_colors(message), channel_id)
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
if discord.accepted_token then
|
||||
mt_client:sendMessage("**" .. player:get_player_name() .. "**" .. " joined the game.", channel_id)
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
if discord.accepted_token then
|
||||
mt_client:sendMessage("**" .. player:get_player_name() .. "**" .. " left the game.", channel_id)
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_shutdown(function()
|
||||
if discord.accepted_token then
|
||||
mt_client:sendMessage("[Server] Server shutting down.", channel_id)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Destroy the file on game boot:
|
||||
local log_file = minetest.get_modpath("discord").."/discord.log"
|
||||
local chat = {}
|
||||
local increment = 0
|
||||
local discord_colour = "#7289DA"
|
||||
os.remove(log_file)
|
||||
|
||||
local function get_discord_messages()
|
||||
local log = io.open(log_file)
|
||||
if log == nil then
|
||||
else
|
||||
log:close()
|
||||
local num_lines = 0
|
||||
for line in io.lines(log_file) do
|
||||
-- Deserialise chat information coming from discord.go
|
||||
local desel = minetest.deserialize(line)
|
||||
if chat[num_lines] == nil then
|
||||
if desel.message == "" then
|
||||
-- Create an empty line if the deserialised message text is empty
|
||||
-- we do this because uploading an image or file with no text causes
|
||||
-- an anomalous message with an empty string
|
||||
chat[num_lines] = ""
|
||||
else
|
||||
-- Colourise the [Discord] text and the <user_name> of the Discord member
|
||||
-- according to their role.
|
||||
chat[num_lines] = minetest.colorize(discord_colour, desel.server) .. " " ..
|
||||
minetest.colorize(desel.colour, desel.nick) .. " " .. desel.message
|
||||
end
|
||||
end
|
||||
num_lines = num_lines + 1
|
||||
end
|
||||
|
||||
for i=increment, #chat do
|
||||
-- Fixes empty lines being sent to chat
|
||||
if chat[i] == "" then
|
||||
else
|
||||
minetest.chat_send_all(chat[i])
|
||||
end
|
||||
end
|
||||
increment = #chat + 1
|
||||
end
|
||||
minetest.after(1, get_discord_messages)
|
||||
end
|
||||
|
||||
minetest.after(2, get_discord_messages)
|
BIN
lib/libcurl.exp
Normal file
BIN
lib/libcurl.exp
Normal file
Binary file not shown.
BIN
lib/libcurl.lib
Normal file
BIN
lib/libcurl.lib
Normal file
Binary file not shown.
128
main.go
Normal file
128
main.go
Normal file
@ -0,0 +1,128 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
// Token for both auth
|
||||
var Token string = ""
|
||||
|
||||
// Channel ID
|
||||
var Channel string = ""
|
||||
|
||||
// Server ID
|
||||
var Server string = ""
|
||||
|
||||
func main() {
|
||||
|
||||
line := 1
|
||||
file, err := os.Open("auth.txt")
|
||||
if err != nil {
|
||||
_, err = os.OpenFile("auth.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
fmt.Println("Failed to load auth.txt, a file named auth.txt has been created.\nThe file should contain the following lines:\n\n<Discord bot token>\n<Discord text channel ID>\n<Discord server ID>")
|
||||
return
|
||||
}
|
||||
fscanner := bufio.NewScanner(file)
|
||||
for fscanner.Scan() {
|
||||
switch line {
|
||||
case 1:
|
||||
Token = fscanner.Text()
|
||||
case 2:
|
||||
Channel = fscanner.Text()
|
||||
case 3:
|
||||
Server = fscanner.Text()
|
||||
}
|
||||
line = line + 1
|
||||
}
|
||||
|
||||
// Create a new Discord session using the provided bot token.
|
||||
dg, err := discordgo.New("Bot " + Token)
|
||||
if err != nil {
|
||||
fmt.Println("Error using provided auth token.", err)
|
||||
return
|
||||
}
|
||||
|
||||
dg.StateEnabled = true
|
||||
|
||||
// Add a handler that manages reading of incoming messages.
|
||||
dg.AddHandler(messageCreate)
|
||||
|
||||
// Open a websocket connection to Discord and begin listening.
|
||||
err = dg.Open()
|
||||
if err != nil {
|
||||
fmt.Println("Failed to open websocket to Discord.,", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Wait here until CTRL-C or other term signal is received.
|
||||
fmt.Println("Bot is now running. Press CTRL-C to exit. \nUse GNU screen or tmux to keep the heartbeat alive; \notherwise the heartbeat may randomly terminate.")
|
||||
sc := make(chan os.Signal, 1)
|
||||
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
|
||||
<-sc
|
||||
|
||||
// Cleanly close down the Discord session.
|
||||
dg.Close()
|
||||
}
|
||||
|
||||
func getRoleColour(userID string, s *discordgo.Session) int {
|
||||
member, _ := s.GuildMember(Server, userID)
|
||||
for _, roleID := range member.Roles {
|
||||
role, err := s.State.Role(Server, roleID)
|
||||
if err == nil {
|
||||
return role.Color
|
||||
}
|
||||
}
|
||||
return int(16777215)
|
||||
}
|
||||
|
||||
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
|
||||
// Ignore all messages created by the bot itself
|
||||
if m.Author.ID == s.State.User.ID {
|
||||
return
|
||||
}
|
||||
|
||||
if m.ChannelID == Channel {
|
||||
member, err := s.State.Member(Server, m.Author.ID)
|
||||
userColour := "#ffffff"
|
||||
|
||||
member, err = s.GuildMember(Server, m.Author.ID)
|
||||
roleColour := getRoleColour(m.Author.ID, s)
|
||||
if roleColour != 0 {
|
||||
blue := roleColour & 0xFF
|
||||
green := (roleColour >> 8) & 0xFF
|
||||
red := (roleColour >> 16) & 0xFF
|
||||
userColour = "#" + strconv.FormatInt(int64(red), 16) + strconv.FormatInt(int64(green), 16) + strconv.FormatInt(int64(blue), 16)
|
||||
}
|
||||
|
||||
serverNick := m.Author.Username
|
||||
if member.Nick != "" {
|
||||
serverNick = member.Nick
|
||||
}
|
||||
|
||||
messageAppend := "return {[\"server\"] = \"[Discord]\", [\"colour\"] = \"" + userColour + "\", [\"nick\"] = \"<" + serverNick + ">\", [\"message\"] = \"" + strings.ReplaceAll(m.Content, "\n", " ") + "\"}\n"
|
||||
|
||||
if m.Message.Attachments != nil {
|
||||
for k := range m.Message.Attachments {
|
||||
messageAppend = messageAppend + "return {[\"server\"] = \"[Discord]\", [\"colour\"] = \"" + userColour + "\", [\"nick\"] = \"<" + serverNick + ">\", [\"message\"] = \"" + m.Message.Attachments[k].URL + "\"}\n"
|
||||
}
|
||||
}
|
||||
|
||||
// Lua may randomly delete this file so always attempt to re-create it.
|
||||
f, err := os.OpenFile("discord.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
f.WriteString(messageAppend)
|
||||
f.Close()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user