From efbba335a7923ef97125215f2da87af8c562ccc5 Mon Sep 17 00:00:00 2001 From: KillerBossOriginal Date: Fri, 4 Aug 2023 19:19:21 +0200 Subject: [PATCH] Add message sending, adding docs --- README.md | 7 +++ lib/src/error_handler.dart | 10 ----- lib/src/errors.dart | 0 lib/src/main.dart | 25 +++++++---- lib/src/requests.dart | 32 ++++++++++++-- lib/src/types.dart | 89 ++++++++++++++++++++++++++++---------- lib/src/webhook.dart | 7 --- 7 files changed, 119 insertions(+), 51 deletions(-) delete mode 100644 lib/src/error_handler.dart delete mode 100644 lib/src/errors.dart diff --git a/README.md b/README.md index ecd18e9..f361c27 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,10 @@ main() { - id: String - token: String url overrides id and token + +## Credits +We took inspiration from [discord.js](https://github.com/discordjs/discord.js) and [Grapes-discord.grapes](https://github.com/BlackdestinyXX/Grapes-discord.grapes). + +### Packages +- [http](https://pub.dev/packages/http) by dart.dev (BSD-3-Clause) +- [events-emitter](https://pub.dev/packages/events_emitter) by drafakiller.com (MIT) diff --git a/lib/src/error_handler.dart b/lib/src/error_handler.dart deleted file mode 100644 index 1727a01..0000000 --- a/lib/src/error_handler.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:http/http.dart'; - -void handleCode(Response res) async { - int code = res.statusCode; - switch (code) { - default: - break; - } - return; -} diff --git a/lib/src/errors.dart b/lib/src/errors.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/src/main.dart b/lib/src/main.dart index feb4fee..2fa1105 100644 --- a/lib/src/main.dart +++ b/lib/src/main.dart @@ -1,15 +1,17 @@ import "dart:async"; import "dart:io"; -import "package:tn_discord/src/types.dart"; - -import "requests.dart"; - import "dart:convert"; import "package:events_emitter/events_emitter.dart"; +import "types.dart"; +import "requests.dart"; + final version = "10"; final apiURL = "https://discord.com/api/v$version"; +/// This function calculate the intent number required from the gateway. +/// [intents] is a list of multiples of two. You can use GatewayIntentBits class. +/// Return a number. int calculateIntents(List intents) { int intentsNumber = 0; @@ -20,6 +22,8 @@ int calculateIntents(List intents) { return intentsNumber; } + +/// The main hub for interacting with the Discord API, and the starting point for any bot. class Client extends EventEmitter { String? token; int intents; @@ -29,8 +33,13 @@ class Client extends EventEmitter { dynamic guilds; dynamic ready; + /// Create a new Client. + /// [intents] Intents to enable for this connection, it's a multiple of two. Client({this.intents = 0}); + /// Logs the client in, establishing a WebSocket connection to Discord. + /// [token] is the token of the account to log in with. + /// Get the token from https://discord.dev login(String token) async { final websocket = await requestWebSocketURL(); @@ -44,7 +53,7 @@ class Client extends EventEmitter { "token": token, "intents": intents, "properties": { - "os": "linux", + "os": Platform.operatingSystem, "browser": "tn_discord", "device": "tn_discord", }, @@ -81,10 +90,10 @@ class Client extends EventEmitter { List gg = []; for (dynamic g in i) { - gg.add(Guild(g)); + gg.add(Guild(sender, g)); } - guilds = GuildManager(gg); + guilds = GuildManager(sender, gg); int n = i.length; @@ -112,7 +121,7 @@ class Client extends EventEmitter { sessionID = event["d"]["session_id"]; break; case "GUILD_CREATE": - guilds.cache.set(event["d"]["id"], Guild(event["d"])); + guilds.cache.set(event["d"]["id"], Guild(sender, event["d"])); if (n > 1) { n--; } else { diff --git a/lib/src/requests.dart b/lib/src/requests.dart index a45fb84..7a84e3b 100644 --- a/lib/src/requests.dart +++ b/lib/src/requests.dart @@ -1,4 +1,5 @@ import 'main.dart'; +import 'types.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; @@ -34,13 +35,13 @@ Future requestWebSocketURL() async { } class Sender { - String? token; + final String? _token; Map headers = {}; - Sender(token) { + Sender(this._token) { headers = { "Content-Type": "application/json", - "Authorization": "Bot $token" + "Authorization": "Bot $_token", }; } @@ -50,4 +51,29 @@ class Sender { res = json.decode(res.body); return res; } + + Future send(Message msg, String cid) async { + final url = Uri.parse("$apiURL/channels/$cid/messages"); + dynamic res = await http.post(url, headers: headers, body: json.encode(msg.exportable())); + res = json.decode(res.body); + return res; + } + + Future fetchGuild(String id) async { + dynamic res = await http.get(Uri.parse("$apiURL/guilds/$id"), headers: headers); + if (res.statusCode != 200) { + throw Exception("Error ${res.statusCode} receiving the guild"); + } + res = json.decode(res.body); + return res; + } + + Future fetchChannel(String id) async { + dynamic res = await http.get(Uri.parse("$apiURL/channels/$id"), headers: headers); + if (res.statusCode != 200) { + throw Exception("Error ${res.statusCode} receiving the channel"); + } + res = json.decode(res.body); + return res; + } } \ No newline at end of file diff --git a/lib/src/types.dart b/lib/src/types.dart index 6ebc41f..9a1ba3e 100644 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -2,7 +2,9 @@ import "dart:convert"; import "package:http/http.dart" as http; import "main.dart"; +import "requests.dart"; +/// A list of all Gateway Intents Bits. class GatewayIntentBits { static const Guilds = 1; static const GuildMembers = 2; @@ -25,15 +27,20 @@ class GatewayIntentBits { static const AutoModerationExecution = 2097152; } +/// A collection of variables. Like NodeJS' Map and discordjs' Collections +/// This will be moved to a separate package in the future. class Collection { final Map _variables = {}; + /// Create a new Collection Collection(); + /// Adds a new element with key [key] and value [value] to the Map. If an element with the same key already exists, the element will be updated. dynamic set(String key, dynamic value) { _variables[key] = value; return value; } + /// Returns [key] from the Map object. If the value that is associated to the provided key is an object, then you will get a reference to that object and any change made to that object will effectively modify it inside the Map. dynamic get(String key) { if (_variables[key] == null) { throw Exception("Variable not found for $key"); @@ -41,43 +48,56 @@ class Collection { return _variables[key]; } + /// Removes [key] from the Map object. void remove(String key) { _variables.remove(key); } + /// Alias of [remove] + void delete(String key) { + remove(key); + } + + /// Sum to the value of [key] to [value]. dynamic add(String key, num value) { _variables[key] += value; return value + _variables[key]; } + /// Subtract [value] from the value of [key]. dynamic subtract(String key, num value) { _variables[key] -= value; - return value - _variables[key]; + return _variables[key] - value; } + /// Returns all the variables as a Map. Map getAll() { return _variables; } + /// Returns all the variables' keys as a List. List keys() { return _variables.keys.toList(); } + /// Returns all the variables' values as a List. List values() { return _variables.values.toList(); } } +/// Represents a guild (aka server) on Discord. class Guild { String id = ''; String name = ''; String? owner; String? description; - ChannelManager channels = ChannelManager([]); - MemberManager members = MemberManager([], ''); - RoleManager roles = RoleManager([]); + late ChannelManager channels; + late MemberManager members; + late RoleManager roles; + final Sender _sender; - Guild(Map data) { + Guild(this._sender, Map data) { id = data["id"]; name = data["name"]; description = data["description"]; @@ -86,9 +106,9 @@ class Guild { if(data["channels"] != null && data["members"] != null && data["roles"] != null) { List cc = []; for (var c in data["channels"]) { - cc.add(Channel(c)); + cc.add(Channel(_sender, c)); } - channels = ChannelManager(cc); + channels = ChannelManager(_sender, cc); List mm = []; for (var m in data["members"]) { @@ -101,25 +121,27 @@ class Guild { rr.add(Role(r)); } roles = RoleManager(rr); + } else { + channels = ChannelManager(_sender, []); + members = MemberManager([], id); + roles = RoleManager([]); } } } class GuildManager { final Collection cache = Collection(); + final Sender _sender; - GuildManager(List guilds) { + GuildManager(this._sender, List guilds) { for (var guild in guilds) { cache.set(guild.id, guild); } } Future fetch(String id) async { - var res = await http.get(Uri.parse("$apiURL/guilds/$id")); - if (res.statusCode != 200) { - throw Exception("Error ${res.statusCode} receiving the guild"); - } - final guild = Guild(json.decode(res.body)); + var res = await _sender.fetchGuild(id); + final guild = Guild(_sender, res); cache.set(guild.id, guild); return guild; } @@ -128,28 +150,32 @@ class GuildManager { class Channel { String id = ''; String name = ''; + final Sender _sender; - Channel(Map data) { + Channel(this._sender, data) { id = data["id"]; name = data["name"]; } + + Future send(Message message) async { + await _sender.send(message, id); + return MessageSent(message, id: id); + } } class ChannelManager { final Collection cache = Collection(); + final Sender _sender; - ChannelManager(List channels) { + ChannelManager(this._sender, List channels) { for (var channel in channels) { cache.set(channel.id, channel); } } Future fetch(String id) async { - var res = await http.get(Uri.parse("$apiURL/channels/$id")); - if (res.statusCode != 200) { - throw Exception("Error ${res.statusCode} receiving the channel"); - } - dynamic channel = Channel(json.decode(res.body)); + var res = await _sender.fetchChannel(id); + dynamic channel = Channel(_sender, res); cache.set(channel.id, channel); return channel; } @@ -157,12 +183,10 @@ class ChannelManager { class Member { String id = ''; - String name = ''; User user = User({}); Member(Map data) { id = data["user"]["id"]; - name = data["user"]["username"]; user = User(data["user"]); } } @@ -253,4 +277,23 @@ class UserManager { } typedef Embed = Map; -typedef Message = Map; + +/// Represents a message on Discord. +class Message { + String? content; + Message({ this.content }); + + exportable() { + return { + "content": content, + }; + } +} + +/// Represents a sent message on Discord. +class MessageSent extends Message { + String id = ''; + MessageSent(Message msg, { required this.id }) { + content = msg.content; + } +} diff --git a/lib/src/webhook.dart b/lib/src/webhook.dart index d93eeb4..1494904 100644 --- a/lib/src/webhook.dart +++ b/lib/src/webhook.dart @@ -1,7 +1,6 @@ import "dart:convert"; import "package:http/http.dart"; -import "error_handler.dart"; import 'requests.dart'; import "types.dart"; import "util.dart"; @@ -25,7 +24,6 @@ class WebhookClient { Map body = {"content": content}; Response res = await sendWH(body, token, id); - handleCode(res); return json.decode(res.body); } @@ -35,7 +33,6 @@ class WebhookClient { Response res = await sendWH(body, token, id); - handleCode(res); return json.decode(res.body); } @@ -43,7 +40,6 @@ class WebhookClient { Map body = {"content": content}; Response res = await editWH(body, token, this.id, id); - handleCode(res); return json.decode(res.body); } @@ -51,20 +47,17 @@ class WebhookClient { Map body = {"content": content, "embeds": embeds}; Response res = await editWH(body, token, this.id, id); - handleCode(res); return json.decode(res.body); } Future> get(String id) async { Response res = await getWH(token, this.id, id); - handleCode(res); return json.decode(res.body); } Future> delete(String id) async { Response res = await deleteWH(token, this.id, id); - handleCode(res); return json.decode(res.body); }