using ArcaneLibs.Extensions; using LibMatrix.EventTypes.Spec; using LibMatrix.EventTypes.Spec.State; using LibMatrix.Helpers; using LibMatrix.Homeservers; using LibMatrix.Services; using LibMatrix.Utilities.Bot.Interfaces; namespace OsuFederatedBeatmapApi.Services; public class FederatedBeatmapApiBot(AuthenticatedHomeserverGeneric hs, ILogger logger, FederatedBeatmapApiBotConfiguration configuration, HomeserverResolverService hsResolver, FederatedBeatmapApiBotAccountDataService accountDataService) : IHostedService { private readonly IEnumerable _commands; private Task _listenerTask; /// Triggered when the application host is ready to start the service. /// Indicates that the start process has been aborted. public async Task StartAsync(CancellationToken cancellationToken) { _listenerTask = Run(cancellationToken); logger.LogInformation("Bot started!"); } private async Task Run(CancellationToken cancellationToken) { Directory.GetFiles("bot_data/cache").ToList().ForEach(File.Delete); var syncHelper = new SyncHelper(hs); List admins = new(); #pragma warning disable CS4014 // We don't care if this doesn't wait Task.Run(async () => { while (!cancellationToken.IsCancellationRequested) { var controlRoomMembers = accountDataService.ControlRoom.GetMembersAsync(); await foreach (var member in controlRoomMembers) { if ((member.TypedContent as RoomMemberEventContent)? .Membership == "join") admins.Add(member.UserId); } await Task.Delay(TimeSpan.FromSeconds(30), cancellationToken); } }, cancellationToken); #pragma warning restore CS4014 foreach (var inviteTask in admins.Select(x=>accountDataService.ControlRoom.InviteUserAsync(x))) await inviteTask; foreach (var inviteTask in admins.Select(x=>accountDataService.LogRoom.InviteUserAsync(x))) await inviteTask; syncHelper.InviteReceivedHandlers.Add(async Task (args) => { var inviteEvent = args.Value.InviteState.Events.FirstOrDefault(x => x.Type == "m.room.member" && x.StateKey == hs.UserId); logger.LogInformation("Got invite to {RoomId} by {Sender} with reason: {Reason}", args.Key, inviteEvent!.Sender, (inviteEvent.TypedContent as RoomMemberEventContent)!.Reason); if (inviteEvent.Sender.EndsWith(":rory.gay") || inviteEvent!.Sender.EndsWith(":conduit.rory.gay") || admins.Contains(inviteEvent.Sender)) { try { var senderProfile = await hs.GetProfileAsync(inviteEvent.Sender); await hs.GetRoom(args.Key).JoinAsync(reason: $"I was invited by {senderProfile.DisplayName ?? inviteEvent.Sender}!"); } catch (Exception e) { logger.LogError("{}", e.ToString()); await hs.GetRoom(args.Key).LeaveAsync(reason: "I was unable to join the room: " + e); } } }); syncHelper.TimelineEventHandlers.Add(async @event => { var room = hs.GetRoom(@event.RoomId); try { logger.LogInformation( "Got timeline event in {}: {}", @event.RoomId, @event.ToJson(indent: true, ignoreNull: true)); if (@event is { Type: "m.room.message", TypedContent: RoomMessageEventContent message }) { } } catch (Exception e) { logger.LogError("{}", e.ToString()); await accountDataService.ControlRoom.SendMessageEventAsync( MessageFormatter.FormatException($"Exception handling event {MessageFormatter.HtmlFormatMention(room.RoomId)}", e)); await accountDataService.LogRoom.SendMessageEventAsync( MessageFormatter.FormatException($"Exception handling event {MessageFormatter.HtmlFormatMention(room.RoomId)}", e)); await using var stream = new MemoryStream(e.ToString().AsBytes().ToArray()); await accountDataService.ControlRoom.SendFileAsync("error.log.cs", stream); await accountDataService.LogRoom.SendFileAsync("error.log.cs", stream); } }); } /// Triggered when the application host is performing a graceful shutdown. /// Indicates that the shutdown process should no longer be graceful. public async Task StopAsync(CancellationToken cancellationToken) { logger.LogInformation("Shutting down bot!"); } }