mirror of
https://github.com/Jetsparrow/jetherald.git
synced 2026-01-21 07:56:09 +03:00
Discord support part 1
This commit is contained in:
parent
041134e58e
commit
1fa9c14666
@ -32,7 +32,7 @@ namespace JetHerald.Commands
|
||||
|
||||
try
|
||||
{
|
||||
var topic = await db.CreateTopic(msg.From.Id, name, descr);
|
||||
var topic = await db.CreateTopic(msg.From.Id, "Telegram", name, descr);
|
||||
return $"created {topic.Name}\n" +
|
||||
$"readToken\n{topic.ReadToken}\n" +
|
||||
$"writeToken\n{topic.WriteToken}\n" +
|
||||
|
||||
116
JetHerald/Commands/DiscordCommands.cs
Normal file
116
JetHerald/Commands/DiscordCommands.cs
Normal file
@ -0,0 +1,116 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using DSharpPlus.CommandsNext;
|
||||
using DSharpPlus.CommandsNext.Attributes;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace JetHerald
|
||||
{
|
||||
[ModuleLifespan(ModuleLifespan.Transient)]
|
||||
public class DiscordCommands : BaseCommandModule
|
||||
{
|
||||
public Db db { private get; set; }
|
||||
|
||||
[Command("createtopic")]
|
||||
[Description("Creates a topic.")]
|
||||
[RequireDirectMessage]
|
||||
public async Task CreateTopic(
|
||||
CommandContext ctx,
|
||||
[Description("The unique name of the new topic.")]
|
||||
string name,
|
||||
[RemainingText, Description("The name displayed in service messages. Defaults to `name`")]
|
||||
string description = null)
|
||||
{
|
||||
if (description == null)
|
||||
description = name;
|
||||
|
||||
await ctx.TriggerTypingAsync();
|
||||
|
||||
try
|
||||
{
|
||||
var topic = await db.CreateTopic((long)ctx.User.Id, "Discord", name, description);
|
||||
await ctx.RespondAsync($"created {topic.Name}\n" +
|
||||
$"readToken\n{topic.ReadToken}\n" +
|
||||
$"writeToken\n{topic.WriteToken}\n" +
|
||||
$"adminToken\n{topic.AdminToken}\n");
|
||||
}
|
||||
catch (MySqlException myDuplicate) when (myDuplicate.Number == 1062)
|
||||
{
|
||||
await ctx.RespondAsync($"topic {name} already exists");
|
||||
}
|
||||
}
|
||||
|
||||
[Command("deletetopic")]
|
||||
[Description("Deletes a topic.")]
|
||||
[RequireDirectMessage]
|
||||
public async Task DeleteTopic(
|
||||
CommandContext ctx,
|
||||
[Description("The name of the topic to be deleted.")]
|
||||
string name,
|
||||
[Description("The admin token of the topic to be deleted.")]
|
||||
string adminToken)
|
||||
{
|
||||
await ctx.TriggerTypingAsync();
|
||||
var changed = await db.DeleteTopic(name, adminToken);
|
||||
if (changed > 0)
|
||||
await ctx.RespondAsync($"deleted {name} and all its subscriptions");
|
||||
else
|
||||
await ctx.RespondAsync($"invalid topic name or admin token");
|
||||
}
|
||||
|
||||
[Command("list")]
|
||||
[Description("List all subscriptions in this channel.")]
|
||||
public async Task ListSubscriptions(CommandContext ctx)
|
||||
{
|
||||
await ctx.TriggerTypingAsync();
|
||||
|
||||
var topics = await db.GetTopicsForChat((long)ctx.Channel.Id, "Discord");
|
||||
|
||||
await ctx.RespondAsync(topics.Any()
|
||||
? "Topics:\n" + string.Join("\n", topics.Select(ListCommand.GetTopicListing))
|
||||
: "No subscriptions active.");
|
||||
}
|
||||
|
||||
[Command("subscribe")]
|
||||
[Description("Subscribes to a topic.")]
|
||||
public async Task Subscribe(
|
||||
CommandContext ctx,
|
||||
[Description("The read token of the token to subscribe to.")]
|
||||
string token
|
||||
)
|
||||
{
|
||||
await ctx.TriggerTypingAsync();
|
||||
|
||||
var topic = await db.GetTopic(token, (long)ctx.Channel.Id, "Discord");
|
||||
|
||||
if (topic == null)
|
||||
await ctx.RespondAsync("topic not found");
|
||||
else if (topic.ChatId == (long)ctx.Channel.Id)
|
||||
await ctx.RespondAsync($"already subscribed to {topic.Name}");
|
||||
else if (topic.ReadToken != token)
|
||||
await ctx.RespondAsync("token mismatch");
|
||||
else
|
||||
{
|
||||
await db.CreateSubscription(topic.TopicId, (long)ctx.Channel.Id, "Discord");
|
||||
await ctx.RespondAsync($"subscribed to {topic.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
[Command("unsubscribe")]
|
||||
[Description("Unsubscribes from a topic.")]
|
||||
public async Task Unsubscribe(
|
||||
CommandContext ctx,
|
||||
[Description("The name of the topic to unsubscribe from.")]
|
||||
string name
|
||||
)
|
||||
{
|
||||
await ctx.TriggerTypingAsync();
|
||||
|
||||
int affected = await db.RemoveSubscription(name, (long)ctx.Channel.Id, "Discord");
|
||||
if (affected >= 1)
|
||||
await ctx.RespondAsync($"unsubscribed from {name}");
|
||||
else
|
||||
await ctx.RespondAsync($"could not find subscription for {name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17,14 +17,14 @@ namespace JetHerald
|
||||
{
|
||||
var msg = messageEventArgs.Message;
|
||||
var chatid = msg.Chat.Id;
|
||||
var topics = await db.GetTopicsForChat(chatid);
|
||||
var topics = await db.GetTopicsForChat(chatid, "Telegram");
|
||||
|
||||
return topics.Any()
|
||||
? "Topics:\n" + string.Join("\n", topics.Select(GetTopicListing))
|
||||
: "No subscriptions active.";
|
||||
}
|
||||
|
||||
static string GetTopicListing(Db.Topic t)
|
||||
internal static string GetTopicListing(Db.Topic t)
|
||||
=> t.Name == t.Description ? t.Name : $"{t.Name}: {t.Description}";
|
||||
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ namespace JetHerald
|
||||
var chatid = args.Message.Chat.Id;
|
||||
var token = cmd.Parameters[0];
|
||||
|
||||
var topic = await db.GetTopic(token, chatid);
|
||||
var topic = await db.GetTopic(token, chatid, "Telegram");
|
||||
|
||||
if (topic == null)
|
||||
return "topic not found";
|
||||
@ -30,7 +30,7 @@ namespace JetHerald
|
||||
return "token mismatch";
|
||||
else
|
||||
{
|
||||
await db.CreateSubscription(topic.TopicId, chatid);
|
||||
await db.CreateSubscription(topic.TopicId, chatid, "Telegram");
|
||||
return $"subscribed to {topic.Name}";
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ namespace JetHerald
|
||||
var msg = messageEventArgs.Message;
|
||||
var chatid = msg.Chat.Id;
|
||||
var topicName = cmd.Parameters[0];
|
||||
int affected = await db.RemoveSubscription(topicName, chatid);
|
||||
int affected = await db.RemoveSubscription(topicName, chatid, "Telegram");
|
||||
if (affected >= 1)
|
||||
return $"unsubscribed from {topicName}";
|
||||
else
|
||||
|
||||
@ -14,4 +14,9 @@
|
||||
public string ProxyPassword { get; set; }
|
||||
public string ProxyLogin { get; set; }
|
||||
}
|
||||
|
||||
public class Discord
|
||||
{
|
||||
public string Token { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ namespace JetHerald
|
||||
{
|
||||
public uint TopicId { get; set; }
|
||||
public long CreatorId { get; set; }
|
||||
public string CreatorService { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string ReadToken { get; set; }
|
||||
@ -23,15 +24,23 @@ namespace JetHerald
|
||||
public bool ExpiryMessageSent { get; set; }
|
||||
|
||||
public long? ChatId { get; set; }
|
||||
public string Service { get; set; }
|
||||
}
|
||||
|
||||
public class ExpiredTopicChat
|
||||
{
|
||||
public long ChatId;
|
||||
public string Service;
|
||||
public string Description;
|
||||
public DateTime ExpiryTime { get; set; }
|
||||
}
|
||||
|
||||
public class ChatData
|
||||
{
|
||||
public long ChatId;
|
||||
public string Service;
|
||||
}
|
||||
|
||||
public async Task<int> DeleteTopic(string name, string adminToken)
|
||||
{
|
||||
using (var c = GetConnection())
|
||||
@ -54,18 +63,18 @@ namespace JetHerald
|
||||
new { name });
|
||||
}
|
||||
|
||||
public async Task<Topic> GetTopic(string token, long chatId)
|
||||
public async Task<Topic> GetTopic(string token, long chatId, string service)
|
||||
{
|
||||
using (var c = GetConnection())
|
||||
return await c.QuerySingleOrDefaultAsync<Topic>(
|
||||
" SELECT t.*, tc.ChatId " +
|
||||
" FROM topic t " +
|
||||
" LEFT JOIN topic_chat tc ON t.TopicId = tc.TopicId AND tc.ChatId = @chatId " +
|
||||
" LEFT JOIN topic_chat tc ON t.TopicId = tc.TopicId AND tc.ChatId = @chatId AND tc.Service = @service " +
|
||||
" WHERE ReadToken = @token",
|
||||
new { token, chatId });
|
||||
new { token, chatId, service });
|
||||
}
|
||||
|
||||
public async Task<Topic> CreateTopic(long userId, string name, string descr)
|
||||
public async Task<Topic> CreateTopic(long userId, string service, string name, string descr)
|
||||
{
|
||||
var t = new Topic
|
||||
{
|
||||
@ -74,60 +83,61 @@ namespace JetHerald
|
||||
Description = descr,
|
||||
ReadToken = TokenHelper.GetToken(),
|
||||
WriteToken = TokenHelper.GetToken(),
|
||||
AdminToken = TokenHelper.GetToken()
|
||||
AdminToken = TokenHelper.GetToken(),
|
||||
Service = service
|
||||
};
|
||||
using (var c = GetConnection())
|
||||
{
|
||||
return await c.QuerySingleOrDefaultAsync<Topic>(
|
||||
" INSERT INTO herald.topic " +
|
||||
" ( CreatorId, Name, Description, ReadToken, WriteToken, AdminToken) " +
|
||||
" ( CreatorId, Name, Description, ReadToken, WriteToken, AdminToken, Service) " +
|
||||
" VALUES " +
|
||||
" (@CreatorId, @Name, @Description, @ReadToken, @WriteToken, @AdminToken); " +
|
||||
" (@CreatorId, @Name, @Description, @ReadToken, @WriteToken, @AdminToken, @Service); " +
|
||||
" SELECT * FROM topic WHERE TopicId = LAST_INSERT_ID(); ",
|
||||
t);
|
||||
}
|
||||
}
|
||||
public async Task<IEnumerable<long>> GetChatIdsForTopic(uint topicid)
|
||||
public async Task<IEnumerable<ChatData>> GetChatIdsForTopic(uint topicid)
|
||||
{
|
||||
using (var c = GetConnection())
|
||||
return await c.QueryAsync<long>(
|
||||
" SELECT ChatId" +
|
||||
return await c.QueryAsync<ChatData>(
|
||||
" SELECT ChatId, Service" +
|
||||
" FROM topic_chat" +
|
||||
" WHERE TopicId = @topicid",
|
||||
new { topicid });
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Topic>> GetTopicsForChat(long chatid)
|
||||
public async Task<IEnumerable<Topic>> GetTopicsForChat(long chatid, string service)
|
||||
{
|
||||
using (var c = GetConnection())
|
||||
return await c.QueryAsync<Topic>(
|
||||
" SELECT t.*" +
|
||||
" FROM topic_chat ct" +
|
||||
" JOIN topic t on t.TopicId = ct.TopicId" +
|
||||
" WHERE ct.ChatId = @chatid",
|
||||
new { chatid });
|
||||
" WHERE ct.ChatId = @chatid AND ct.Service = @service",
|
||||
new { chatid, service });
|
||||
}
|
||||
|
||||
public async Task CreateSubscription(uint topicId, long chatId)
|
||||
public async Task CreateSubscription(uint topicId, long chatId, string service)
|
||||
{
|
||||
using (var c = GetConnection())
|
||||
await c.ExecuteAsync(
|
||||
" INSERT INTO topic_chat" +
|
||||
" (ChatId, TopicId )" +
|
||||
" (ChatId, TopicId, Service)" +
|
||||
" VALUES" +
|
||||
" (@chatId, @topicId)",
|
||||
new { topicId, chatId });
|
||||
" (@chatId, @topicId, @service)",
|
||||
new { topicId, chatId, service });
|
||||
}
|
||||
|
||||
public async Task<int> RemoveSubscription(string topicName, long chatId)
|
||||
public async Task<int> RemoveSubscription(string topicName, long chatId, string service)
|
||||
{
|
||||
using (var c = GetConnection())
|
||||
return await c.ExecuteAsync(
|
||||
" DELETE tc " +
|
||||
" FROM topic_chat tc" +
|
||||
" JOIN topic t ON tc.TopicId = t.TopicId " +
|
||||
" WHERE t.Name = @topicName AND tc.ChatId = @chatId;",
|
||||
new { topicName, chatId });
|
||||
" WHERE t.Name = @topicName AND tc.ChatId = @chatId AND tc.Service = @service;",
|
||||
new { topicName, chatId, service });
|
||||
}
|
||||
|
||||
public Task AddExpiry(string topicName, int addedTime)
|
||||
@ -156,7 +166,7 @@ namespace JetHerald
|
||||
{
|
||||
using var c = GetConnection();
|
||||
return c.QueryAsync<ExpiredTopicChat>(
|
||||
" SELECT tc.ChatId, t.Description, t.ExpiryTime" +
|
||||
" SELECT tc.ChatId, tc.Service, t.Description, t.ExpiryTime" +
|
||||
" FROM topic_chat tc" +
|
||||
" INNER JOIN topic t ON t.TopicId = tc.TopicId" +
|
||||
" WHERE t.ExpiryTime < CURRENT_TIMESTAMP() AND NOT t.ExpiryMessageSent",
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dapper" Version="1.60.6" />
|
||||
<PackageReference Include="DSharpPlus" Version="4.0.0" />
|
||||
<PackageReference Include="DSharpPlus.CommandsNext" Version="4.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.1" />
|
||||
<PackageReference Include="MySql.Data" Version="8.0.17" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="4.8.4" />
|
||||
|
||||
38
JetHerald/JetHeraldBot.Discord.cs
Normal file
38
JetHerald/JetHeraldBot.Discord.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System.Threading.Tasks;
|
||||
using DSharpPlus;
|
||||
using DSharpPlus.CommandsNext;
|
||||
|
||||
namespace JetHerald
|
||||
{
|
||||
public partial class JetHeraldBot
|
||||
{
|
||||
DiscordClient DiscordBot { get; set; }
|
||||
|
||||
async Task InitDiscord()
|
||||
{
|
||||
DiscordBot = new DiscordClient(new()
|
||||
{
|
||||
Token = DiscordConfig.Token,
|
||||
TokenType = TokenType.Bot,
|
||||
Intents = DiscordIntents.AllUnprivileged,
|
||||
LoggerFactory = LoggerFactory
|
||||
});
|
||||
|
||||
var commands = DiscordBot.UseCommandsNext(new CommandsNextConfiguration()
|
||||
{
|
||||
StringPrefixes = new[] { "!" },
|
||||
Services = ServiceProvider
|
||||
});
|
||||
|
||||
commands.RegisterCommands<DiscordCommands>();
|
||||
|
||||
await DiscordBot.ConnectAsync();
|
||||
}
|
||||
|
||||
|
||||
private async Task SendMessageToDiscordChannel(long chatId, string formatted)
|
||||
{
|
||||
await DiscordBot.SendMessageAsync(await DiscordBot.GetChannelAsync((ulong)chatId), formatted);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,20 +12,27 @@ using System.Threading;
|
||||
|
||||
namespace JetHerald
|
||||
{
|
||||
public class JetHeraldBot
|
||||
public partial class JetHeraldBot
|
||||
{
|
||||
Db Db { get; set; }
|
||||
Options.Telegram Config { get; }
|
||||
Options.Telegram TelegramConfig { get; }
|
||||
Options.Discord DiscordConfig { get; }
|
||||
ILogger<JetHeraldBot> Log { get; }
|
||||
ILoggerFactory LoggerFactory { get; }
|
||||
IServiceProvider ServiceProvider { get; }
|
||||
|
||||
public JetHeraldBot(Db db, IOptions<Options.Telegram> cfg, ILogger<JetHeraldBot> log)
|
||||
public JetHeraldBot(Db db, IOptions<Options.Telegram> telegramCfg, IOptions<Options.Discord> discordCfg, ILogger<JetHeraldBot> log, ILoggerFactory loggerFactory, IServiceProvider serviceProvider)
|
||||
{
|
||||
Db = db;
|
||||
Config = cfg.Value;
|
||||
TelegramConfig = telegramCfg.Value;
|
||||
DiscordConfig = discordCfg.Value;
|
||||
|
||||
Log = log;
|
||||
LoggerFactory = loggerFactory;
|
||||
ServiceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
TelegramBotClient Client { get; set; }
|
||||
TelegramBotClient TelegramBot { get; set; }
|
||||
ChatCommandRouter Commands;
|
||||
CancellationTokenSource HeartbeatCancellation;
|
||||
Task HeartbeatTask;
|
||||
@ -33,17 +40,17 @@ namespace JetHerald
|
||||
|
||||
public async Task Init()
|
||||
{
|
||||
if (Config.UseProxy)
|
||||
if (TelegramConfig.UseProxy)
|
||||
{
|
||||
var httpProxy = new WebProxy(Config.ProxyUrl)
|
||||
{ Credentials = new NetworkCredential(Config.ProxyLogin, Config.ProxyPassword) };
|
||||
Client = new TelegramBotClient(Config.ApiKey, httpProxy);
|
||||
var httpProxy = new WebProxy(TelegramConfig.ProxyUrl)
|
||||
{ Credentials = new NetworkCredential(TelegramConfig.ProxyLogin, TelegramConfig.ProxyPassword) };
|
||||
TelegramBot = new TelegramBotClient(TelegramConfig.ApiKey, httpProxy);
|
||||
}
|
||||
else
|
||||
{
|
||||
Client = new TelegramBotClient(Config.ApiKey);
|
||||
TelegramBot = new TelegramBotClient(TelegramConfig.ApiKey);
|
||||
}
|
||||
Me = await Client.GetMeAsync();
|
||||
Me = await TelegramBot.GetMeAsync();
|
||||
|
||||
Commands = new ChatCommandRouter(Me.Username, Log);
|
||||
Commands.Add(new CreateTopicCommand(Db), "createtopic");
|
||||
@ -55,13 +62,16 @@ namespace JetHerald
|
||||
HeartbeatCancellation = new();
|
||||
HeartbeatTask = CheckHeartbeats(HeartbeatCancellation.Token);
|
||||
|
||||
Client.OnMessage += BotOnMessageReceived;
|
||||
Client.StartReceiving();
|
||||
TelegramBot.OnMessage += BotOnMessageReceived;
|
||||
TelegramBot.StartReceiving();
|
||||
|
||||
await InitDiscord();
|
||||
}
|
||||
|
||||
public async Task Stop()
|
||||
{
|
||||
Client.StopReceiving();
|
||||
await DiscordBot.DisconnectAsync();
|
||||
TelegramBot.StopReceiving();
|
||||
HeartbeatCancellation.Cancel();
|
||||
try
|
||||
{
|
||||
@ -82,7 +92,7 @@ namespace JetHerald
|
||||
foreach (var chatSent in await Db.GetExpiredTopics(token))
|
||||
{
|
||||
var formatted = $"!{chatSent.Description}!:\nTimeout expired at {chatSent.ExpiryTime}";
|
||||
await Client.SendTextMessageAsync(chatSent.ChatId, formatted, cancellationToken: token);
|
||||
await TelegramBot.SendTextMessageAsync(chatSent.ChatId, formatted, cancellationToken: token);
|
||||
}
|
||||
|
||||
await Db.MarkExpiredTopics(token);
|
||||
@ -94,7 +104,12 @@ namespace JetHerald
|
||||
var chatIds = await Db.GetChatIdsForTopic(topic.TopicId);
|
||||
var formatted = $"!{topic.Description}!:\nA heartbeat has been sent.";
|
||||
foreach (var c in chatIds)
|
||||
await Client.SendTextMessageAsync(c, formatted);
|
||||
{
|
||||
if (c.Service == "Telegram")
|
||||
await TelegramBot.SendTextMessageAsync(c.ChatId, formatted);
|
||||
else if (c.Service == "Discord")
|
||||
await SendMessageToDiscordChannel(c.ChatId, formatted);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PublishMessage(Db.Topic topic, string message)
|
||||
@ -102,7 +117,12 @@ namespace JetHerald
|
||||
var chatIds = await Db.GetChatIdsForTopic(topic.TopicId);
|
||||
var formatted = $"|{topic.Description}|:\n{message}";
|
||||
foreach (var c in chatIds)
|
||||
await Client.SendTextMessageAsync(c, formatted);
|
||||
{
|
||||
if (c.Service == "Telegram")
|
||||
await TelegramBot.SendTextMessageAsync(c.ChatId, formatted);
|
||||
else if (c.Service == "Discord")
|
||||
await SendMessageToDiscordChannel(c.ChatId, formatted);
|
||||
}
|
||||
}
|
||||
|
||||
async void BotOnMessageReceived(object sender, MessageEventArgs messageEventArgs)
|
||||
@ -115,7 +135,7 @@ namespace JetHerald
|
||||
{
|
||||
var reply = await Commands.Execute(sender, messageEventArgs);
|
||||
if (reply != null)
|
||||
await Client.SendTextMessageAsync(
|
||||
await TelegramBot.SendTextMessageAsync(
|
||||
chatId: msg.Chat.Id,
|
||||
text: reply,
|
||||
replyToMessageId: msg.MessageId);
|
||||
|
||||
@ -21,6 +21,7 @@ namespace JetHerald
|
||||
{
|
||||
services.Configure<Options.ConnectionStrings>(Configuration.GetSection("ConnectionStrings"));
|
||||
services.Configure<Options.Telegram>(Configuration.GetSection("Telegram"));
|
||||
services.Configure<Options.Discord>(Configuration.GetSection("Discord"));
|
||||
services.AddSingleton<Db>();
|
||||
services.AddSingleton<JetHeraldBot>();
|
||||
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
|
||||
@ -31,7 +32,7 @@ namespace JetHerald
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
var bot = app.ApplicationServices.GetService<JetHeraldBot>();
|
||||
bot.Init().Wait();
|
||||
bot.Init().GetAwaiter().GetResult();
|
||||
app.UsePathBase(Configuration.GetValue<string>("PathBase"));
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user