Add message sending, adding docs

This commit is contained in:
KillerBossOriginal 2023-08-04 19:19:21 +02:00
parent 4f2da16910
commit efbba335a7
7 changed files with 119 additions and 51 deletions

View file

@ -17,3 +17,10 @@ main() {
- id: String - id: String
- token: String - token: String
url overrides id and token 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)

View file

@ -1,10 +0,0 @@
import 'package:http/http.dart';
void handleCode(Response res) async {
int code = res.statusCode;
switch (code) {
default:
break;
}
return;
}

View file

View file

@ -1,15 +1,17 @@
import "dart:async"; import "dart:async";
import "dart:io"; import "dart:io";
import "package:tn_discord/src/types.dart";
import "requests.dart";
import "dart:convert"; import "dart:convert";
import "package:events_emitter/events_emitter.dart"; import "package:events_emitter/events_emitter.dart";
import "types.dart";
import "requests.dart";
final version = "10"; final version = "10";
final apiURL = "https://discord.com/api/v$version"; 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<int> intents) { int calculateIntents(List<int> intents) {
int intentsNumber = 0; int intentsNumber = 0;
@ -20,6 +22,8 @@ int calculateIntents(List<int> intents) {
return intentsNumber; return intentsNumber;
} }
/// The main hub for interacting with the Discord API, and the starting point for any bot.
class Client extends EventEmitter { class Client extends EventEmitter {
String? token; String? token;
int intents; int intents;
@ -29,8 +33,13 @@ class Client extends EventEmitter {
dynamic guilds; dynamic guilds;
dynamic ready; dynamic ready;
/// Create a new Client.
/// [intents] Intents to enable for this connection, it's a multiple of two.
Client({this.intents = 0}); 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 { login(String token) async {
final websocket = await requestWebSocketURL(); final websocket = await requestWebSocketURL();
@ -44,7 +53,7 @@ class Client extends EventEmitter {
"token": token, "token": token,
"intents": intents, "intents": intents,
"properties": { "properties": {
"os": "linux", "os": Platform.operatingSystem,
"browser": "tn_discord", "browser": "tn_discord",
"device": "tn_discord", "device": "tn_discord",
}, },
@ -81,10 +90,10 @@ class Client extends EventEmitter {
List<Guild> gg = []; List<Guild> gg = [];
for (dynamic g in i) { 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; int n = i.length;
@ -112,7 +121,7 @@ class Client extends EventEmitter {
sessionID = event["d"]["session_id"]; sessionID = event["d"]["session_id"];
break; break;
case "GUILD_CREATE": 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) { if (n > 1) {
n--; n--;
} else { } else {

View file

@ -1,4 +1,5 @@
import 'main.dart'; import 'main.dart';
import 'types.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'dart:convert'; import 'dart:convert';
@ -34,13 +35,13 @@ Future<String> requestWebSocketURL() async {
} }
class Sender { class Sender {
String? token; final String? _token;
Map<String, String> headers = {}; Map<String, String> headers = {};
Sender(token) { Sender(this._token) {
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": "Bot $token" "Authorization": "Bot $_token",
}; };
} }
@ -50,4 +51,29 @@ class Sender {
res = json.decode(res.body); res = json.decode(res.body);
return res; 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;
}
} }

View file

@ -2,7 +2,9 @@
import "dart:convert"; import "dart:convert";
import "package:http/http.dart" as http; import "package:http/http.dart" as http;
import "main.dart"; import "main.dart";
import "requests.dart";
/// A list of all Gateway Intents Bits.
class GatewayIntentBits { class GatewayIntentBits {
static const Guilds = 1; static const Guilds = 1;
static const GuildMembers = 2; static const GuildMembers = 2;
@ -25,15 +27,20 @@ class GatewayIntentBits {
static const AutoModerationExecution = 2097152; 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 { class Collection {
final Map<String, dynamic> _variables = {}; final Map<String, dynamic> _variables = {};
/// Create a new Collection
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) { dynamic set(String key, dynamic value) {
_variables[key] = value; _variables[key] = value;
return 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) { dynamic get(String key) {
if (_variables[key] == null) { if (_variables[key] == null) {
throw Exception("Variable not found for $key"); throw Exception("Variable not found for $key");
@ -41,43 +48,56 @@ class Collection {
return _variables[key]; return _variables[key];
} }
/// Removes [key] from the Map object.
void remove(String key) { void remove(String key) {
_variables.remove(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) { dynamic add(String key, num value) {
_variables[key] += value; _variables[key] += value;
return value + _variables[key]; return value + _variables[key];
} }
/// Subtract [value] from the value of [key].
dynamic subtract(String key, num value) { dynamic subtract(String key, num value) {
_variables[key] -= value; _variables[key] -= value;
return value - _variables[key]; return _variables[key] - value;
} }
/// Returns all the variables as a Map.
Map<String, dynamic> getAll() { Map<String, dynamic> getAll() {
return _variables; return _variables;
} }
/// Returns all the variables' keys as a List.
List<String> keys() { List<String> keys() {
return _variables.keys.toList(); return _variables.keys.toList();
} }
/// Returns all the variables' values as a List.
List<dynamic> values() { List<dynamic> values() {
return _variables.values.toList(); return _variables.values.toList();
} }
} }
/// Represents a guild (aka server) on Discord.
class Guild { class Guild {
String id = ''; String id = '';
String name = ''; String name = '';
String? owner; String? owner;
String? description; String? description;
ChannelManager channels = ChannelManager([]); late ChannelManager channels;
MemberManager members = MemberManager([], ''); late MemberManager members;
RoleManager roles = RoleManager([]); late RoleManager roles;
final Sender _sender;
Guild(Map data) { Guild(this._sender, Map data) {
id = data["id"]; id = data["id"];
name = data["name"]; name = data["name"];
description = data["description"]; description = data["description"];
@ -86,9 +106,9 @@ class Guild {
if(data["channels"] != null && data["members"] != null && data["roles"] != null) { if(data["channels"] != null && data["members"] != null && data["roles"] != null) {
List<Channel> cc = []; List<Channel> cc = [];
for (var c in data["channels"]) { for (var c in data["channels"]) {
cc.add(Channel(c)); cc.add(Channel(_sender, c));
} }
channels = ChannelManager(cc); channels = ChannelManager(_sender, cc);
List<Member> mm = []; List<Member> mm = [];
for (var m in data["members"]) { for (var m in data["members"]) {
@ -101,25 +121,27 @@ class Guild {
rr.add(Role(r)); rr.add(Role(r));
} }
roles = RoleManager(rr); roles = RoleManager(rr);
} else {
channels = ChannelManager(_sender, []);
members = MemberManager([], id);
roles = RoleManager([]);
} }
} }
} }
class GuildManager { class GuildManager {
final Collection cache = Collection(); final Collection cache = Collection();
final Sender _sender;
GuildManager(List<Guild> guilds) { GuildManager(this._sender, List<Guild> guilds) {
for (var guild in guilds) { for (var guild in guilds) {
cache.set(guild.id, guild); cache.set(guild.id, guild);
} }
} }
Future<Guild> fetch(String id) async { Future<Guild> fetch(String id) async {
var res = await http.get(Uri.parse("$apiURL/guilds/$id")); var res = await _sender.fetchGuild(id);
if (res.statusCode != 200) { final guild = Guild(_sender, res);
throw Exception("Error ${res.statusCode} receiving the guild");
}
final guild = Guild(json.decode(res.body));
cache.set(guild.id, guild); cache.set(guild.id, guild);
return guild; return guild;
} }
@ -128,28 +150,32 @@ class GuildManager {
class Channel { class Channel {
String id = ''; String id = '';
String name = ''; String name = '';
final Sender _sender;
Channel(Map data) { Channel(this._sender, data) {
id = data["id"]; id = data["id"];
name = data["name"]; name = data["name"];
} }
Future<MessageSent> send(Message message) async {
await _sender.send(message, id);
return MessageSent(message, id: id);
}
} }
class ChannelManager { class ChannelManager {
final Collection cache = Collection(); final Collection cache = Collection();
final Sender _sender;
ChannelManager(List<Channel> channels) { ChannelManager(this._sender, List<Channel> channels) {
for (var channel in channels) { for (var channel in channels) {
cache.set(channel.id, channel); cache.set(channel.id, channel);
} }
} }
Future<Channel> fetch(String id) async { Future<Channel> fetch(String id) async {
var res = await http.get(Uri.parse("$apiURL/channels/$id")); var res = await _sender.fetchChannel(id);
if (res.statusCode != 200) { dynamic channel = Channel(_sender, res);
throw Exception("Error ${res.statusCode} receiving the channel");
}
dynamic channel = Channel(json.decode(res.body));
cache.set(channel.id, channel); cache.set(channel.id, channel);
return channel; return channel;
} }
@ -157,12 +183,10 @@ class ChannelManager {
class Member { class Member {
String id = ''; String id = '';
String name = '';
User user = User({}); User user = User({});
Member(Map data) { Member(Map data) {
id = data["user"]["id"]; id = data["user"]["id"];
name = data["user"]["username"];
user = User(data["user"]); user = User(data["user"]);
} }
} }
@ -253,4 +277,23 @@ class UserManager {
} }
typedef Embed = Map<String, String>; typedef Embed = Map<String, String>;
typedef Message = Map<String, String>;
/// 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;
}
}

View file

@ -1,7 +1,6 @@
import "dart:convert"; import "dart:convert";
import "package:http/http.dart"; import "package:http/http.dart";
import "error_handler.dart";
import 'requests.dart'; import 'requests.dart';
import "types.dart"; import "types.dart";
import "util.dart"; import "util.dart";
@ -25,7 +24,6 @@ class WebhookClient {
Map<String, String> body = {"content": content}; Map<String, String> body = {"content": content};
Response res = await sendWH(body, token, id); Response res = await sendWH(body, token, id);
handleCode(res);
return json.decode(res.body); return json.decode(res.body);
} }
@ -35,7 +33,6 @@ class WebhookClient {
Response res = await sendWH(body, token, id); Response res = await sendWH(body, token, id);
handleCode(res);
return json.decode(res.body); return json.decode(res.body);
} }
@ -43,7 +40,6 @@ class WebhookClient {
Map<String, String> body = {"content": content}; Map<String, String> body = {"content": content};
Response res = await editWH(body, token, this.id, id); Response res = await editWH(body, token, this.id, id);
handleCode(res);
return json.decode(res.body); return json.decode(res.body);
} }
@ -51,20 +47,17 @@ class WebhookClient {
Map body = {"content": content, "embeds": embeds}; Map body = {"content": content, "embeds": embeds};
Response res = await editWH(body, token, this.id, id); Response res = await editWH(body, token, this.id, id);
handleCode(res);
return json.decode(res.body); return json.decode(res.body);
} }
Future<Map<String, dynamic>> get(String id) async { Future<Map<String, dynamic>> get(String id) async {
Response res = await getWH(token, this.id, id); Response res = await getWH(token, this.id, id);
handleCode(res);
return json.decode(res.body); return json.decode(res.body);
} }
Future<Map<String, dynamic>> delete(String id) async { Future<Map<String, dynamic>> delete(String id) async {
Response res = await deleteWH(token, this.id, id); Response res = await deleteWH(token, this.id, id);
handleCode(res);
return json.decode(res.body); return json.decode(res.body);
} }