diff --git a/nyxx.commander/lib/src/CommandContext.dart b/nyxx.commander/lib/src/CommandContext.dart index 12315e4d..aac89da3 100644 --- a/nyxx.commander/lib/src/CommandContext.dart +++ b/nyxx.commander/lib/src/CommandContext.dart @@ -25,9 +25,6 @@ class CommandContext { /// Shard on which message was sent int get shardId => this.guild != null ? this.guild!.shard.id : 0; - /// Returns shard on which message was sent - Shard get shard => this.client.shardManager.shards.toList()[shardId]; - /// Substring by which command was matched final String commandMatcher; @@ -139,7 +136,7 @@ class CommandContext { Future> awaitEmojis(Message msg, Duration duration){ final collectedEmoji = {}; return Future>(() async { - await for (final event in (msg.client as Nyxx).onMessageReactionAdded.where((evnt) => evnt.message != null && evnt.message!.id == msg.id)) { + await for (final event in client.onMessageReactionAdded.where((evnt) => evnt.message != null && evnt.message!.id == msg.id)) { if (collectedEmoji.containsKey(event.emoji)) { // TODO: NNBD: weird stuff var value = collectedEmoji[event.emoji]; @@ -161,7 +158,7 @@ class CommandContext { /// Waits for first [TypingEvent] and returns it. If timed out returns null. /// Can listen to specific user by specifying [user] Future waitForTyping(User user, {Duration timeout = const Duration(seconds: 30)}) => - Future(() => (user.client as Nyxx).onTyping.firstWhere((e) => e.user == user && e.channel == this.channel)).timeout(timeout, onTimeout: () => null); + Future(() => client.onTyping.firstWhere((e) => e.user == user && e.channel == this.channel)).timeout(timeout, onTimeout: () => null); /// Gets all context channel messages that satisfies [predicate]. /// diff --git a/nyxx.commander/pubspec.yaml b/nyxx.commander/pubspec.yaml index 29d7f4e5..f2888de3 100644 --- a/nyxx.commander/pubspec.yaml +++ b/nyxx.commander/pubspec.yaml @@ -1,5 +1,5 @@ name: nyxx_commander -version: 1.1.0-dev.6 +version: 1.1.0-dev.1 description: A Discord library for Dart. homepage: https://github.com/l7ssha/nyxx repository: https://github.com/l7ssha/nyxx @@ -7,9 +7,13 @@ documentation: https://github.com/l7ssha/nyxx/wiki issue_tracker: https://github.com/l7ssha/nyxx/issue environment: - sdk: '>=2.12.0 <2.13.0' + sdk: '>=2.12.0-51.0.dev <3.0.0' dependencies: http: "^0.13.0" - logging: "^1.0.1" - nyxx: "^1.1.0-dev.5" + logging: "^1.0.0-nullsafety.0" + nyxx: "^1.1.0-dev.1" + +dependency_overrides: + nyxx: + path: "../nyxx" diff --git a/nyxx.extensions/lib/src/message_resolver/message_resolver.dart b/nyxx.extensions/lib/src/message_resolver/message_resolver.dart index 3531b4e6..1e3942ab 100644 --- a/nyxx.extensions/lib/src/message_resolver/message_resolver.dart +++ b/nyxx.extensions/lib/src/message_resolver/message_resolver.dart @@ -38,7 +38,7 @@ extension MessageResolverExtension on Message { return ""; } - return MessageResolver(this.client, + return MessageResolver(this.client as Nyxx, userTagHandling: userTagHandling, roleTagHandling: roleTagHandling, everyoneTagHandling: everyoneTagHandling, diff --git a/nyxx.extensions/pubspec.yaml b/nyxx.extensions/pubspec.yaml index d987b721..cbf06286 100644 --- a/nyxx.extensions/pubspec.yaml +++ b/nyxx.extensions/pubspec.yaml @@ -1,5 +1,5 @@ name: nyxx_extensions -version: 1.1.0-dev.3 +version: 1.1.0-dev.2 description: Extensions for Nyxx library homepage: https://github.com/l7ssha/nyxx repository: https://github.com/l7ssha/nyxx @@ -7,8 +7,12 @@ documentation: https://github.com/l7ssha/nyxx/wiki issue_tracker: https://github.com/l7ssha/nyxx/issue environment: - sdk: '>=2.12.0 <2.13.0' + sdk: '>=2.12.0-51.0.dev <3.0.0' dependencies: + nyxx: "^1.1.0-dev.1" http: "^0.13.0" - nyxx: "1.1.0-dev.4" + +dependency_overrides: + nyxx: + path: "../nyxx" \ No newline at end of file diff --git a/nyxx/lib/src/Nyxx.dart b/nyxx/lib/src/Nyxx.dart index bb4f6239..1a387c89 100644 --- a/nyxx/lib/src/Nyxx.dart +++ b/nyxx/lib/src/Nyxx.dart @@ -1,5 +1,176 @@ part of nyxx; +abstract class INyxx implements Disposable { + _HttpHandler get _http; + _HttpEndpoints get _httpEndpoints; + + ClientOptions get _options; + CacheOptions get _cacheOptions; + + String get _token; + + /// All of the guilds the bot is in. Can be empty or can miss guilds on (READY_EVENT). + Cache get guilds; + + /// All of the channels the bot can see. + ChannelCache get channels; + + /// All of the users the bot can see. Does not have offline users + /// without `forceFetchUsers` enabled. + Cache get users; + + /// Returns handler for all available REST API action. + IHttpEndpoints get httpEndpoints => this._httpEndpoints; + + /// Emitted when a successful HTTP response is received. + late final StreamController _onHttpResponse; + + /// Emitted when a HTTP request failed. + late final StreamController _onHttpError; + + /// Sent when the client is ratelimited, either by the ratelimit handler itself, + /// or when a 429 is received. + late final StreamController _onRatelimited; + + /// Emitted when a successful HTTP response is received. + late Stream onHttpResponse; + + /// Emitted when a HTTP request failed. + late Stream onHttpError; + + /// Sent when the client is ratelimited, either by the ratelimit handler itself, + /// or when a 429 is received. + late Stream onRatelimited; +} + +/// Lightweight client which do not start ws connections. +class NyxxRest extends INyxx { + @override + final String _token; + + final DateTime _startTime = DateTime.now(); + + @override + late final ClientOptions _options; + @override + late final CacheOptions _cacheOptions; + @override + late final _HttpHandler _http; + @override + late final _HttpEndpoints _httpEndpoints; + + /// When identifying to the gateway, you have to specify an intents parameter which + /// allows you to conditionally subscribe to pre-defined "intents", groups of events defined by Discord. + /// If you do not specify a certain intent, you will not receive any of the gateway events that are batched into that group. + /// Since api v8 its required upon connecting to gateway. + final int intents; + + /// The current bot user. + late ClientUser self; + + /// The bot"s OAuth2 app. + late ClientOAuth2Application app; + + /// All of the guilds the bot is in. Can be empty or can miss guilds on (READY_EVENT). + @override + late final Cache guilds; + + /// All of the channels the bot can see. + @override + late final ChannelCache channels; + + /// All of the users the bot can see. Does not have offline users + /// without `forceFetchUsers` enabled. + @override + late final Cache users; + + /// True if client is ready. + bool ready = false; + + /// The current version of `nyxx` + final String version = Constants.version; + + /// Logger instance + final Logger _logger = Logger("Client"); + + /// Gets an bot invite link with zero permissions + String get inviteLink => app.getInviteUrl(); + + /// Can be used to edit options after client initialised. Used by Nyxx.interactions to enable raw events + ClientOptions get options => this._options; + + /// Creates and logs in a new client. If [ignoreExceptions] is true (by default is) + /// isolate will ignore all exceptions and continue to work. + NyxxRest(this._token, this.intents, + {ClientOptions? options, + CacheOptions? cacheOptions, + bool ignoreExceptions = true, + bool useDefaultLogger = true, + Level? defaultLoggerLogLevel}) { + + if (useDefaultLogger) { + Logger.root.level = defaultLoggerLogLevel ?? Level.ALL; + + Logger.root.onRecord.listen((LogRecord rec) { + print( + "[${rec.time}] [${rec.level.name}] [${rec.loggerName}] ${rec.message}"); + }); + } + + this._logger.info("Starting bot with pid: $pid"); + + if (_token.isEmpty) { + throw MissingTokenError(); + } + + if (!Platform.isWindows) { + ProcessSignal.sigterm.watch().forEach((event) async { + await this.dispose(); + }); + } + + ProcessSignal.sigint.watch().forEach((event) async { + await this.dispose(); + }); + + if (ignoreExceptions) { + Isolate.current.setErrorsFatal(false); + + final errorsPort = ReceivePort(); + errorsPort.listen((err) { + _logger.severe("ERROR: ${err[0]} \n ${err[1]}"); + }); + Isolate.current.addErrorListener(errorsPort.sendPort); + } + + this._options = options ?? ClientOptions(); + this._cacheOptions = cacheOptions ?? CacheOptions(); + + this.guilds = _SnowflakeCache(); + this.channels = ChannelCache._new(); + this.users = _SnowflakeCache(); + + this._http = _HttpHandler._new(this); + this._httpEndpoints = _HttpEndpoints._new(this); + + this._onHttpError = StreamController.broadcast(); + this.onHttpError = _onHttpError.stream; + + this._onHttpResponse = StreamController.broadcast(); + this.onHttpResponse = _onHttpResponse.stream; + + this._onRatelimited = StreamController.broadcast(); + this.onRatelimited = _onRatelimited.stream; + } + + @override + Future dispose() async { + await this._onHttpResponse.close(); + await this._onHttpError.close(); + await this._onRatelimited.close(); + } +} + /// The main place to start with interacting with the Discord API and creating discord bot. /// From there you can subscribe to various [Stream]s to listen to [Events](https://github.com/l7ssha/nyxx/wiki/EventList) /// and fetch data from API with provided methods or get cached data. @@ -17,63 +188,16 @@ part of nyxx; /// }); /// ``` /// or setup `CommandsFramework` and `Voice`. -class Nyxx implements Disposable { - final String _token; - final DateTime _startTime = DateTime.now(); - - late final ClientOptions _options; - late final CacheOptions _cacheOptions; - +class Nyxx extends NyxxRest { late final _ConnectionManager _ws; // ignore: unused_field late final _EventController _events; - late final _HttpHandler _http; - late final _HttpEndpoints _httpEndpoints; - - /// When identifying to the gateway, you have to specify an intents parameter which - /// allows you to conditionally subscribe to pre-defined "intents", groups of events defined by Discord. - /// If you do not specify a certain intent, you will not receive any of the gateway events that are batched into that group. - /// Since api v8 its required upon connecting to gateway. - final int intents; - - /// The current bot user. - late ClientUser self; - - /// The bot"s OAuth2 app. - late ClientOAuth2Application app; - - /// All of the guilds the bot is in. Can be empty or can miss guilds on (READY_EVENT). - late final Cache guilds; - - /// All of the channels the bot can see. - late final ChannelCache channels; - - /// All of the users the bot can see. Does not have offline users - /// without `forceFetchUsers` enabled. - late final Cache users; - - /// True if client is ready. - bool ready = false; - - /// The current version of `nyxx` - final String version = Constants.version; - /// Current client"s shard late ShardManager shardManager; /// Emitted when a shard is disconnected from the websocket. late Stream onDisconnect; - /// Emitted when a successful HTTP response is received. - late Stream onHttpResponse; - - /// Emitted when a HTTP request failed. - late Stream onHttpError; - - /// Sent when the client is ratelimited, either by the ratelimit handler itself, - /// or when a 429 is received. - late Stream onRatelimited; - /// Emitted when the client is ready. Should be sent only once. late Stream onReady; @@ -177,75 +301,22 @@ class Nyxx implements Disposable { /// Emitted when a bot removes all instances of a given emoji from the reactions of a message late Stream onMessageReactionRemoveEmoji; - /// Logger instance - final Logger _logger = Logger("Client"); - - /// Gets an bot invite link with zero permissions - String get inviteLink => app.getInviteUrl(); - - /// Can be used to edit options after client initialised. Used by Nyxx.interactions to enable raw events - ClientOptions get options => this._options; - - /// Returns handler for all available REST API action. - IHttpEndpoints get httpEndpoints => this._httpEndpoints; - /// Creates and logs in a new client. If [ignoreExceptions] is true (by default is) /// isolate will ignore all exceptions and continue to work. - Nyxx(this._token, this.intents, + Nyxx(String token, int intents, {ClientOptions? options, CacheOptions? cacheOptions, bool ignoreExceptions = true, bool useDefaultLogger = true, - Level? defaultLoggerLogLevel}) { - if (useDefaultLogger) { - Logger.root.level = defaultLoggerLogLevel ?? Level.ALL; - - Logger.root.onRecord.listen((LogRecord rec) { - print( - "[${rec.time}] [${rec.level.name}] [${rec.loggerName}] ${rec.message}"); - }); - } - - this._logger.info("Starting bot with pid: $pid"); - - if (_token.isEmpty) { - throw MissingTokenError(); - } - - if (!Platform.isWindows) { - ProcessSignal.sigterm.watch().forEach((event) async { - await this.dispose(); - }); - } - - ProcessSignal.sigint.watch().forEach((event) async { - await this.dispose(); - }); - - if (ignoreExceptions) { - Isolate.current.setErrorsFatal(false); - - final errorsPort = ReceivePort(); - errorsPort.listen((err) { - _logger.severe("ERROR: ${err[0]}; ${err[1]}"); - }); - Isolate.current.addErrorListener(errorsPort.sendPort); - } - - this._options = options ?? ClientOptions(); - this._cacheOptions = cacheOptions ?? CacheOptions(); - - this.guilds = _SnowflakeCache(); - this.channels = ChannelCache._new(); - this.users = _SnowflakeCache(); - - this._http = _HttpHandler._new(this); - this._httpEndpoints = _HttpEndpoints._new(this); - + Level? defaultLoggerLogLevel}) : + super(token, intents, options: options, cacheOptions: cacheOptions, + ignoreExceptions: ignoreExceptions, useDefaultLogger: useDefaultLogger, + defaultLoggerLogLevel: defaultLoggerLogLevel + ) { this._events = _EventController(this); this.onSelfMention = this .onMessageReceived - .where((event) => event.message.mentions.any((element) => element.id == this.self.id)); + .where((event) => event.message.mentions.contains(this.self)); this.onDmReceived = this.onMessageReceived.where((event) => event.message is DMMessage); @@ -280,6 +351,29 @@ class Nyxx implements Disposable { Future fetchUser(Snowflake userId) => this._httpEndpoints.fetchUser(userId); + // /// Creates new guild with provided builder. + // /// Only for bots with less than 10 guilds otherwise it will return Future with error. + // /// + // /// ``` + // /// var guildBuilder = GuildBuilder() + // /// ..name = "Example Guild" + // /// ..roles = [RoleBuilder()..name = "Example Role] + // /// var newGuild = await client.createGuild(guildBuilder); + // /// ``` + // Future createGuild(GuildBuilder builder) async { + // if (this.guilds.count >= 10) { + // return Future.error(ArgumentError("Guild cannot be created if bot is in 10 or more guilds")); + // } + // + // final response = await this._http._execute(BasicRequest._new("/guilds", method: "POST")); + // + // if (response is HttpResponseSuccess) { + // return Guild._new(this, response.jsonBody as Map); + // } + // + // return Future.error(response); + // } + /// Gets a webhook by its id and/or token. /// If token is supplied authentication is not needed. Future fetchWebhook(Snowflake id, {String token = ""}) => diff --git a/nyxx/lib/src/core/Invite.dart b/nyxx/lib/src/core/Invite.dart index e75333c3..37c0e9b6 100644 --- a/nyxx/lib/src/core/Invite.dart +++ b/nyxx/lib/src/core/Invite.dart @@ -18,7 +18,7 @@ class Invite { late final Cacheable? targetUser; /// Reference to bot instance - final Nyxx client; + final INyxx client; /// Returns url to invite String get url => "https://discord.gg/$code"; @@ -93,7 +93,7 @@ class InviteWithMeta extends Invite { return ageValidity && expiryValidity; } - InviteWithMeta._new(Map raw, Nyxx client) : super._new(raw, client) { + InviteWithMeta._new(Map raw, INyxx client) : super._new(raw, client) { this.createdAt = DateTime.parse(raw["created_at"] as String); this.temporary = raw["temporary"] as bool; this.uses = raw["uses"] as int; diff --git a/nyxx/lib/src/core/auditlogs/AuditLog.dart b/nyxx/lib/src/core/auditlogs/AuditLog.dart index 2fb3e04f..6a622c34 100644 --- a/nyxx/lib/src/core/auditlogs/AuditLog.dart +++ b/nyxx/lib/src/core/auditlogs/AuditLog.dart @@ -17,7 +17,7 @@ class AuditLog { Iterable filter(bool Function(AuditLogEntry) test) => entries.values.where(test); - AuditLog._new(Map raw, Nyxx client) { + AuditLog._new(Map raw, INyxx client) { webhooks = {}; users = {}; entries = {}; diff --git a/nyxx/lib/src/core/auditlogs/AuditLogEntry.dart b/nyxx/lib/src/core/auditlogs/AuditLogEntry.dart index 81015237..89c4bdea 100644 --- a/nyxx/lib/src/core/auditlogs/AuditLogEntry.dart +++ b/nyxx/lib/src/core/auditlogs/AuditLogEntry.dart @@ -22,7 +22,7 @@ class AuditLogEntry extends SnowflakeEntity { /// The reason for the change String? reason; - AuditLogEntry._new(Map raw, Nyxx client) : super(Snowflake(raw["id"] as String)) { + AuditLogEntry._new(Map raw, INyxx client) : super(Snowflake(raw["id"] as String)) { this.targetId = raw["targetId"] as String; this.changes = [ diff --git a/nyxx/lib/src/core/channel/Channel.dart b/nyxx/lib/src/core/channel/Channel.dart index 38cee7f7..11299a35 100644 --- a/nyxx/lib/src/core/channel/Channel.dart +++ b/nyxx/lib/src/core/channel/Channel.dart @@ -8,13 +8,13 @@ abstract class IChannel extends SnowflakeEntity implements Disposable { late final ChannelType channelType; /// Reference to client - final Nyxx client; + final INyxx client; IChannel._new(this.client, Map raw): super(Snowflake(raw["id"])){ this.channelType = ChannelType.from(raw["type"] as int); } - factory IChannel._deserialize(Nyxx client, Map raw, [Snowflake? guildId]) { + factory IChannel._deserialize(INyxx client, Map raw, [Snowflake? guildId]) { final type = raw["type"] as int; switch (type) { @@ -43,7 +43,7 @@ abstract class IChannel extends SnowflakeEntity implements Disposable { } class _InternalChannel extends GuildChannel { - _InternalChannel._new(Nyxx client, Map raw, [Snowflake? guildId]): super._new(client, raw, guildId); + _InternalChannel._new(INyxx client, Map raw, [Snowflake? guildId]): super._new(client, raw, guildId); } /// Enum for possible channel types diff --git a/nyxx/lib/src/core/channel/DMChannel.dart b/nyxx/lib/src/core/channel/DMChannel.dart index 5d58c669..f1980333 100644 --- a/nyxx/lib/src/core/channel/DMChannel.dart +++ b/nyxx/lib/src/core/channel/DMChannel.dart @@ -19,7 +19,7 @@ class DMChannel extends IChannel implements TextChannel { /// Returns other user in chat if channel is not group dm. Will throw [ArgumentError] if channel is group dm. User get participant => !this.isGroupDM ? participants.first : throw new ArgumentError("Channel is not direct DM"); - DMChannel._new(Nyxx client, Map raw): super._new(client, raw) { + DMChannel._new(INyxx client, Map raw): super._new(client, raw) { if (raw["recipients"] != null) { this.participants = [ for (final userRaw in raw["recipients"]) diff --git a/nyxx/lib/src/core/channel/guild/CategoryGuildChannel.dart b/nyxx/lib/src/core/channel/guild/CategoryGuildChannel.dart index 12a1380f..759159df 100644 --- a/nyxx/lib/src/core/channel/guild/CategoryGuildChannel.dart +++ b/nyxx/lib/src/core/channel/guild/CategoryGuildChannel.dart @@ -1,5 +1,5 @@ part of nyxx; class CategoryGuildChannel extends GuildChannel { - CategoryGuildChannel._new(Nyxx client, Map raw, [Snowflake? guildId]): super._new(client, raw, guildId); + CategoryGuildChannel._new(INyxx client, Map raw, [Snowflake? guildId]): super._new(client, raw, guildId); } diff --git a/nyxx/lib/src/core/channel/guild/GuildChannel.dart b/nyxx/lib/src/core/channel/guild/GuildChannel.dart index 6ea74af8..f4ce4d49 100644 --- a/nyxx/lib/src/core/channel/guild/GuildChannel.dart +++ b/nyxx/lib/src/core/channel/guild/GuildChannel.dart @@ -19,7 +19,7 @@ abstract class GuildChannel extends IChannel { /// Permission override for channel late final List permissionOverrides; - GuildChannel._new(Nyxx client, Map raw, [Snowflake? guildId]) : super._new(client, raw) { + GuildChannel._new(INyxx client, Map raw, [Snowflake? guildId]) : super._new(client, raw) { this.name = raw["name"] as String; this.position = raw["position"] as int; diff --git a/nyxx/lib/src/core/channel/guild/TextGuildChannel.dart b/nyxx/lib/src/core/channel/guild/TextGuildChannel.dart index 29562861..37804773 100644 --- a/nyxx/lib/src/core/channel/guild/TextGuildChannel.dart +++ b/nyxx/lib/src/core/channel/guild/TextGuildChannel.dart @@ -27,7 +27,7 @@ class TextGuildChannel extends GuildChannel implements TextChannel { // Used to create infinite typing loop Timer? _typing; - TextGuildChannel._new(Nyxx client, Map raw, [Snowflake? guildId]) : super._new(client, raw, guildId) { + TextGuildChannel._new(INyxx client, Map raw, [Snowflake? guildId]) : super._new(client, raw, guildId) { this.topic = raw["topic"] as String?; this.slowModeThreshold = raw["rate_limit_per_user"] as int? ?? 0; } diff --git a/nyxx/lib/src/core/channel/guild/VoiceChannel.dart b/nyxx/lib/src/core/channel/guild/VoiceChannel.dart index ba72c71d..e555da55 100644 --- a/nyxx/lib/src/core/channel/guild/VoiceChannel.dart +++ b/nyxx/lib/src/core/channel/guild/VoiceChannel.dart @@ -7,7 +7,7 @@ class VoiceGuildChannel extends GuildChannel { /// The channel's user limit. late final int? userLimit; - VoiceGuildChannel._new(Nyxx client, Map raw, [Snowflake? guildId]) : super._new(client, raw, guildId) { + VoiceGuildChannel._new(INyxx client, Map raw, [Snowflake? guildId]) : super._new(client, raw, guildId) { this.bitrate = raw["bitrate"] as int?; this.userLimit = raw["user_limit"] as int?; } @@ -18,8 +18,12 @@ class VoiceGuildChannel extends GuildChannel { /// Connects client to channel void connect({bool selfMute = false, bool selfDeafen = false}) { + if (this.client is! Nyxx) { + throw new UnsupportedError("Cannot connect with NyxxRest"); + } + try { - final shard = this.client.shardManager.shards.firstWhere((element) => element.guilds.contains(this.guild.id)); + final shard = (this.client as Nyxx).shardManager.shards.firstWhere((element) => element.guilds.contains(this.guild.id)); shard.changeVoiceState(this.guild.id, this.id, selfMute: selfMute, selfDeafen: selfDeafen); } on Error { @@ -29,8 +33,12 @@ class VoiceGuildChannel extends GuildChannel { /// Disconnects use from channel. void disconnect() { + if (this.client is! Nyxx) { + throw new UnsupportedError("Cannot connect with NyxxRest"); + } + try { - final shard = this.client.shardManager.shards.firstWhere((element) => element.guilds.contains(this.guild.id)); + final shard = (this.client as Nyxx).shardManager.shards.firstWhere((element) => element.guilds.contains(this.guild.id)); shard.changeVoiceState(this.guild.id, null); } on Error { diff --git a/nyxx/lib/src/core/guild/Ban.dart b/nyxx/lib/src/core/guild/Ban.dart index 9f4de570..fbb479ef 100644 --- a/nyxx/lib/src/core/guild/Ban.dart +++ b/nyxx/lib/src/core/guild/Ban.dart @@ -8,7 +8,7 @@ class Ban { /// Banned user late final User user; - Ban._new(Map raw, Nyxx client) { + Ban._new(Map raw, INyxx client) { this.reason = raw["reason"] as String; this.user = User._new(client, raw["user"] as Map); } diff --git a/nyxx/lib/src/core/guild/Guild.dart b/nyxx/lib/src/core/guild/Guild.dart index 14baf4c1..0f43b27a 100644 --- a/nyxx/lib/src/core/guild/Guild.dart +++ b/nyxx/lib/src/core/guild/Guild.dart @@ -2,7 +2,7 @@ part of nyxx; class Guild extends SnowflakeEntity { /// Reference to [Nyxx] instance - Nyxx client; + final INyxx client; /// The guild's name. late final String name; @@ -97,7 +97,13 @@ class Guild extends SnowflakeEntity { Role get everyoneRole => roles.values.firstWhere((r) => r.name == "@everyone"); /// Returns member object for bot user - Member? get selfMember => members[client.self.id]; + Member? get selfMember { + if (this.client is! Nyxx) { + throw new UnsupportedError("Cannot use this property with NyxxRest"); + } + + return members[(client as Nyxx).self.id]; + } /// File upload limit for channel in bytes. int get fileUploadLimit { @@ -115,7 +121,13 @@ class Guild extends SnowflakeEntity { } /// Returns this guilds shard - Shard get shard => client.shardManager.shards.firstWhere((_shard) => _shard.guilds.contains(this.id)); + Shard get shard { + if (this.client is! Nyxx) { + throw new UnsupportedError("Cannot use this property with NyxxRest"); + } + + return (client as Nyxx).shardManager.shards.firstWhere((_shard) => _shard.guilds.contains(this.id)); + } Guild._new(this.client, Map raw, [bool guildCreate = false]) : super(Snowflake(raw["id"])) { this.name = raw["name"] as String; diff --git a/nyxx/lib/src/core/guild/GuildPreview.dart b/nyxx/lib/src/core/guild/GuildPreview.dart index 465b2bb7..996de582 100644 --- a/nyxx/lib/src/core/guild/GuildPreview.dart +++ b/nyxx/lib/src/core/guild/GuildPreview.dart @@ -4,7 +4,7 @@ part of nyxx; /// This endpoint is only for Public guilds. class GuildPreview extends SnowflakeEntity { /// Reference to client - final Nyxx client; + final INyxx client; /// Guild name late final String name; diff --git a/nyxx/lib/src/core/guild/Role.dart b/nyxx/lib/src/core/guild/Role.dart index f8aa43ae..52cac2ca 100644 --- a/nyxx/lib/src/core/guild/Role.dart +++ b/nyxx/lib/src/core/guild/Role.dart @@ -2,7 +2,7 @@ part of nyxx; class Role extends SnowflakeEntity implements Mentionable { /// Reference to client - final Nyxx client; + final INyxx client; /// Cachealble or guild attached to this role instance late final Cacheable guild; diff --git a/nyxx/lib/src/core/guild/Webhook.dart b/nyxx/lib/src/core/guild/Webhook.dart index e506fbb6..3aec3bd6 100644 --- a/nyxx/lib/src/core/guild/Webhook.dart +++ b/nyxx/lib/src/core/guild/Webhook.dart @@ -62,7 +62,7 @@ class Webhook extends SnowflakeEntity implements IMessageAuthor { String get tag => ""; /// Reference to [Nyxx] object - final Nyxx client; + final INyxx client; Webhook._new(Map raw, this.client) : super(Snowflake(raw["id"] as String)) { this.name = raw["name"] as String?; diff --git a/nyxx/lib/src/core/message/GuildEmoji.dart b/nyxx/lib/src/core/message/GuildEmoji.dart index 4fe26d44..b2ebeaed 100644 --- a/nyxx/lib/src/core/message/GuildEmoji.dart +++ b/nyxx/lib/src/core/message/GuildEmoji.dart @@ -32,7 +32,7 @@ class GuildEmojiPartial extends IGuildEmoji implements IEmoji { class GuildEmoji extends GuildEmojiPartial implements IEmoji { /// Reference to client - final Nyxx client; + final INyxx client; /// Reference to guild where emoji belongs to late final Cacheable guild; diff --git a/nyxx/lib/src/core/message/Message.dart b/nyxx/lib/src/core/message/Message.dart index 434cf3d1..beb812ac 100644 --- a/nyxx/lib/src/core/message/Message.dart +++ b/nyxx/lib/src/core/message/Message.dart @@ -2,7 +2,7 @@ part of nyxx; abstract class Message extends SnowflakeEntity implements Disposable { /// Reference to bot instance - final Nyxx client; + final INyxx client; /// The message's content. late String content; @@ -52,7 +52,7 @@ abstract class Message extends SnowflakeEntity implements Disposable { /// Message reply late final ReferencedMessage? referencedMessage; - factory Message._deserialize(Nyxx client, Map raw) { + factory Message._deserialize(INyxx client, Map raw) { if (raw["guild_id"] != null) { return GuildMessage._new(client, raw); } @@ -185,7 +185,7 @@ class DMMessage extends Message { String get url => "https://discordapp.com/channels/@me" "/${this.channel.id}/${this.id}"; - DMMessage._new(Nyxx client, Map raw) : super._new(client, raw) { + DMMessage._new(INyxx client, Map raw) : super._new(client, raw) { final user = client.users[Snowflake(raw["author"]["id"])]; if (user == null) { @@ -228,7 +228,7 @@ class GuildMessage extends Message { /// Role mentions in this message late final List> roleMentions; - GuildMessage._new(Nyxx client, Map raw) : super._new(client, raw) { + GuildMessage._new(INyxx client, Map raw) : super._new(client, raw) { if (raw["message_reference"] != null) { this.crossPostReference = MessageReference._new( raw["message_reference"] as Map, client); diff --git a/nyxx/lib/src/core/message/MessageReference.dart b/nyxx/lib/src/core/message/MessageReference.dart index 96b32cb6..248886d5 100644 --- a/nyxx/lib/src/core/message/MessageReference.dart +++ b/nyxx/lib/src/core/message/MessageReference.dart @@ -11,7 +11,7 @@ class MessageReference { /// Original guild late final Cacheable? guild; - MessageReference._new(Map raw, Nyxx client) { + MessageReference._new(Map raw, INyxx client) { this.channel = _ChannelCacheable(client, Snowflake(raw["channel_id"])); if (raw["message_id"] != null) { diff --git a/nyxx/lib/src/core/message/ReferencedMessage.dart b/nyxx/lib/src/core/message/ReferencedMessage.dart index fc42814b..9c758b02 100644 --- a/nyxx/lib/src/core/message/ReferencedMessage.dart +++ b/nyxx/lib/src/core/message/ReferencedMessage.dart @@ -16,7 +16,7 @@ class ReferencedMessage { /// True if references message exists and is available bool get exists => !isDeleted && !isBackendFetchError; - ReferencedMessage._new(Nyxx client, Map raw) { + ReferencedMessage._new(INyxx client, Map raw) { if (!raw.containsKey("referenced_message")) { this.message = null; this.isBackendFetchError = true; diff --git a/nyxx/lib/src/core/user/Member.dart b/nyxx/lib/src/core/user/Member.dart index eec6ada1..921534a5 100644 --- a/nyxx/lib/src/core/user/Member.dart +++ b/nyxx/lib/src/core/user/Member.dart @@ -2,7 +2,7 @@ part of nyxx; class Member extends SnowflakeEntity { /// Reference to client - final Nyxx client; + final INyxx client; /// [Cacheable] for this [Guild] member late final Cacheable user; diff --git a/nyxx/lib/src/core/user/User.dart b/nyxx/lib/src/core/user/User.dart index b2bf5d76..e79f9a26 100644 --- a/nyxx/lib/src/core/user/User.dart +++ b/nyxx/lib/src/core/user/User.dart @@ -3,7 +3,7 @@ part of nyxx; /// Represents a single user of Discord, either a human or a bot, outside of any specific guild's context. class User extends SnowflakeEntity with Mentionable, IMessageAuthor implements ISend { /// Reference to client - final Nyxx client; + final INyxx client; /// The user's username. @override diff --git a/nyxx/lib/src/core/voice/VoiceState.dart b/nyxx/lib/src/core/voice/VoiceState.dart index bd577d86..7e4ec7ea 100644 --- a/nyxx/lib/src/core/voice/VoiceState.dart +++ b/nyxx/lib/src/core/voice/VoiceState.dart @@ -33,7 +33,7 @@ class VoiceState { /// Whether this user's camera is enabled late final bool selfVideo; - VoiceState._new(Nyxx client, Map raw) { + VoiceState._new(INyxx client, Map raw) { if (raw["channel_id"] != null) { this.channel = _ChannelCacheable(client, Snowflake(raw["channel_id"])); } else { diff --git a/nyxx/lib/src/internal/_EventController.dart b/nyxx/lib/src/internal/_EventController.dart index b08ec897..fbd6bb70 100644 --- a/nyxx/lib/src/internal/_EventController.dart +++ b/nyxx/lib/src/internal/_EventController.dart @@ -5,16 +5,6 @@ class _EventController implements Disposable { /// Emitted when a shard is disconnected from the websocket. late final StreamController onDisconnect; - /// Emitted when a successful HTTP response is received. - late final StreamController onHttpResponse; - - /// Emitted when a HTTP request failed. - late final StreamController onHttpError; - - /// Sent when the client is ratelimited, either by the ratelimit handler itself, - /// or when a 429 is received. - late final StreamController onRatelimited; - /// Emitted when the client is ready. late final StreamController onReady; @@ -116,15 +106,6 @@ class _EventController implements Disposable { this.onDisconnect = StreamController.broadcast(); _client.onDisconnect = this.onDisconnect.stream; - this.onHttpResponse = StreamController.broadcast(); - _client.onHttpResponse = this.onHttpResponse.stream; - - this.onHttpError = StreamController.broadcast(); - _client.onHttpError = this.onHttpError.stream; - - this.onRatelimited = StreamController.broadcast(); - _client.onRatelimited = this.onRatelimited.stream; - this.onReady = StreamController.broadcast(); _client.onReady = this.onReady.stream; @@ -225,9 +206,6 @@ class _EventController implements Disposable { @override Future dispose() async { await this.onDisconnect.close(); - await this.onHttpResponse.close(); - await this.onHttpError.close(); - await this.onRatelimited.close(); await this.onGuildUpdate.close(); await this.onReady.close(); await this.onMessageReceived.close(); diff --git a/nyxx/lib/src/internal/_HttpEndpoints.dart b/nyxx/lib/src/internal/_HttpEndpoints.dart index 66807642..9aa963e6 100644 --- a/nyxx/lib/src/internal/_HttpEndpoints.dart +++ b/nyxx/lib/src/internal/_HttpEndpoints.dart @@ -275,7 +275,7 @@ abstract class IHttpEndpoints { class _HttpEndpoints implements IHttpEndpoints { late final _HttpHandler _httpClient; - final Nyxx _client; + final INyxx _client; _HttpEndpoints._new(this._client) { this._httpClient = this._client._http; diff --git a/nyxx/lib/src/internal/cache/Cacheable.dart b/nyxx/lib/src/internal/cache/Cacheable.dart index 4e483470..e3e6f94a 100644 --- a/nyxx/lib/src/internal/cache/Cacheable.dart +++ b/nyxx/lib/src/internal/cache/Cacheable.dart @@ -4,7 +4,7 @@ part of nyxx; /// Always provides [id] of entity. `download()` method tries to get entity from API and returns it upon success or /// throws Error if something happens in the process. abstract class Cacheable { - final Nyxx _client; + final INyxx _client; /// Id of entity final T id; @@ -39,7 +39,7 @@ abstract class Cacheable { class _RoleCacheable extends Cacheable { final Cacheable guild; - _RoleCacheable(Nyxx client, Snowflake id, this.guild): super._new(client, id); + _RoleCacheable(INyxx client, Snowflake id, this.guild): super._new(client, id); @override Future download() async => this._fetchGuildRole(); @@ -68,7 +68,7 @@ class _RoleCacheable extends Cacheable { } class _ChannelCacheable extends Cacheable { - _ChannelCacheable(Nyxx client, Snowflake id): super._new(client, id); + _ChannelCacheable(INyxx client, Snowflake id): super._new(client, id); @override T? getFromCache() => this._client.channels[this.id] as T?; @@ -78,7 +78,7 @@ class _ChannelCacheable extends Cacheable { } class _GuildCacheable extends Cacheable { - _GuildCacheable(Nyxx client, Snowflake id): super._new(client, id); + _GuildCacheable(INyxx client, Snowflake id): super._new(client, id); @override Guild? getFromCache() => this._client.guilds[this.id]; @@ -88,7 +88,7 @@ class _GuildCacheable extends Cacheable { } class _UserCacheable extends Cacheable { - _UserCacheable(Nyxx client, Snowflake id): super._new(client, id); + _UserCacheable(INyxx client, Snowflake id): super._new(client, id); @override Future download() => _client._httpEndpoints.fetchUser(this.id); @@ -100,7 +100,7 @@ class _UserCacheable extends Cacheable { class _MemberCacheable extends Cacheable { final Cacheable guild; - _MemberCacheable(Nyxx client, Snowflake id, this.guild): super._new(client, id); + _MemberCacheable(INyxx client, Snowflake id, this.guild): super._new(client, id); @override Future download() => @@ -121,7 +121,7 @@ class _MemberCacheable extends Cacheable { class _MessageCacheable extends Cacheable { final Cacheable channel; - _MessageCacheable(Nyxx client, Snowflake id, this.channel) : super._new(client, id); + _MessageCacheable(INyxx client, Snowflake id, this.channel) : super._new(client, id); @override Future download() async { diff --git a/nyxx/lib/src/internal/http/HttpBucket.dart b/nyxx/lib/src/internal/http/HttpBucket.dart index 2d771731..7de11343 100644 --- a/nyxx/lib/src/internal/http/HttpBucket.dart +++ b/nyxx/lib/src/internal/http/HttpBucket.dart @@ -22,7 +22,7 @@ class _HttpBucket { final waitTime = resetAt!.millisecondsSinceEpoch - now.millisecondsSinceEpoch; if (waitTime > 0) { - _httpHandler.client._events.onRatelimited.add(RatelimitEvent._new(request, true)); + _httpHandler.client._onRatelimited.add(RatelimitEvent._new(request, true)); _httpHandler._logger.warning( "Rate limited internally on endpoint: ${request.uri}. Trying to send request again in $waitTime ms..."); @@ -49,7 +49,7 @@ class _HttpBucket { final responseBody = jsonDecode(await response.stream.bytesToString()); final retryAfter = ((responseBody["retry_after"] as double) * 1000).round(); - _httpHandler.client._events.onRatelimited.add(RatelimitEvent._new(request, false, response)); + _httpHandler.client._onRatelimited.add(RatelimitEvent._new(request, false, response)); _httpHandler._logger.warning( "Rate limited via 429 on endpoint: ${request.uri}. Trying to send request again in $retryAfter ms..."); diff --git a/nyxx/lib/src/internal/http/HttpHandler.dart b/nyxx/lib/src/internal/http/HttpHandler.dart index f8d98328..ebc88e9d 100644 --- a/nyxx/lib/src/internal/http/HttpHandler.dart +++ b/nyxx/lib/src/internal/http/HttpHandler.dart @@ -6,7 +6,7 @@ class _HttpHandler { final Logger _logger = Logger("Http"); late final _HttpClient _httpClient; - final Nyxx client; + final INyxx client; _HttpHandler._new(this.client) { this._noRateBucket = _HttpBucket(Uri.parse("noratelimit"), this); @@ -38,14 +38,14 @@ class _HttpHandler { final responseSuccess = HttpResponseSuccess._new(response); await responseSuccess._finalize(); - client._events.onHttpResponse.add(HttpResponseEvent._new(responseSuccess)); + client._onHttpResponse.add(HttpResponseEvent._new(responseSuccess)); return responseSuccess; } final responseError = HttpResponseError._new(response); await responseError._finalize(); - client._events.onHttpError.add(HttpErrorEvent._new(responseError)); + client._onHttpError.add(HttpErrorEvent._new(responseError)); return responseError; } } diff --git a/nyxx/lib/src/internal/http/_HttpClient.dart b/nyxx/lib/src/internal/http/_HttpClient.dart index 94cab059..c6510f23 100644 --- a/nyxx/lib/src/internal/http/_HttpClient.dart +++ b/nyxx/lib/src/internal/http/_HttpClient.dart @@ -6,7 +6,7 @@ class _HttpClient extends http.BaseClient { final http.Client _innerClient = http.Client(); // ignore: public_member_api_docs - _HttpClient(Nyxx client) { + _HttpClient(INyxx client) { this._authHeader = { "Authorization" : "Bot ${client._token}" }; diff --git a/nyxx/pubspec.yaml b/nyxx/pubspec.yaml index 5c523d64..3fd06478 100644 --- a/nyxx/pubspec.yaml +++ b/nyxx/pubspec.yaml @@ -1,5 +1,5 @@ name: nyxx -version: 1.1.0-dev.6 +version: 1.1.0-dev.3 description: A Discord library for Dart. homepage: https://github.com/l7ssha/nyxx repository: https://github.com/l7ssha/nyxx @@ -7,12 +7,12 @@ documentation: https://github.com/l7ssha/nyxx/wiki issue_tracker: https://github.com/l7ssha/nyxx/issue environment: - sdk: '>=2.12.0 <2.13.0' + sdk: '>=2.12.0-51.0.dev <3.0.0' dependencies: + logging: "^1.0.0-nullsafety.0" http: "^0.13.0" - logging: "^1.0.1" - path: "^1.8.0" + path: "^1.8.0-nullsafety.3" dev_dependencies: - test: "^1.16.8" + test: "^1.16.2"