I have 1 problem I need make bot what if I reaction to message, bot resend this message to other discord channel, but I can't take message from message which I reaction. What I need write in ???, I try write message, message.GetOrDownloadAsync, message.DownloadAsync, reaction.Message, but I don't have message, I have something like System.Func1[System.Threading.Tasks.Task1[Discord.IUserMessage]]
private async Task MainAsyns()
{
_client.ReactionAdded += HandleReaction;
}
private async Task HandleReaction(Cacheable<IUserMessage, ulong> message, Cacheable<IMessageChannel, ulong> channel, SocketReaction reaction)
{
var toOtherChanel = _client.GetChannel(_toOtherChanelId) as IMessageChannel;
var msg = ???;
if (!_client.GetUser(reaction.UserId).IsBot)
{
if (reaction.Emote.Name == "\uD83D\uDC4D")
{
await toOtherChanel.SendMessageAsync($"Message is {msg}");
}
}
}
Related
I wrote the telegram bot, and it works perfectly, BUT I still didn't understand how can I write the unit test for this? I mean I have to make a fake messages from my bot. I realized that I need to use moq instead real telegramm bot client. But I still didn't understand how exactly it may look like?
Here is my method, I want UnitTesting:
private readonly string _token = "<token value>";
private ITelegramBotClient _client;
public async void Comunicate()
{
_client = new TelegramBotClient(_token);
var receiverOptions = new ReceiverOptions
{
AllowedUpdates = { }
};
_client.StartReceiving(HandleUpdateAsync, HandleErrorAsync, receiverOptions, cancellationToken: _cts.Token);
var me = await _client.GetMeAsync();
Console.WriteLine($"Start listening for #{me.Username}");
Console.ReadLine();
async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
{
if (update.Type != UpdateType.Message)
return;
if (update.Message!.Type != MessageType.Text)
return;
var chatId = update.Message.Chat.Id;
var messageText = update.Message.Text;
// here messages processed, I want to process fake messages without telegramm API using
string valuteCode = "";
string requredDate = "";
BotActions botActions = ProcessTextMessage(messageText, ref valuteCode, ref requredDate);
string replyMessage = GetReply(botActions, requredDate, valuteCode);
Console.WriteLine($"Received a '{messageText}' message in chat {chatId}.");
Message sentMessage = await botClient.SendTextMessageAsync(
chatId: chatId,
text: replyMessage,
cancellationToken: cancellationToken);
}
Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken)
{
var ErrorMessage = exception switch
{
ApiRequestException apiRequestException
=> $"Telegram API Error:\n[{apiRequestException.ErrorCode}]\n{apiRequestException.Message}",
_ => exception.ToString()
};
Console.WriteLine(ErrorMessage);
return Task.CompletedTask;
}
In Unit test I just have to call method Comunicate() and send ho handler fake testing messages without real messages sending in telegram. Unfortunatelly I don't understand how to do it. I know that I can create another bot just for testing this one but this not the way I want to go.
I would be grateful for any help.
I am doing a command for my bot to edit a message sent by it
But I need to get the message to edit it
I found a function that obtains it but returns an IMessage which does not have the ModifyAsync() function
This is my code:
[Command("editmessage")]
public async Task EditMessage(string sMessageId, string TextMessage) {
ulong MessageId = Convert.ToUInt64(sMessageId);
var Message = Context.Channel.GetMessageAsync(MessageId);
await Message.ModifyAsync(msg => msg.Content = TextMessage); //This gives me an error because "IMessage" does not contain a definition for "ModifyAsync"
}
Is there a way to go from IMessage to IUserMessage? (IUserMessage if it has the ModifyAsync() function)
Forgive me for my bad english
What you need to do is await for the operation to complete asynchronously via await. Don't use .Result here as that is a blocking operation.
[Command("editmessage")]
public async Task EditMessage(string messageId, string textMessage)
{
var id = Convert.ToUInt64(messageId);
var msg = await Context.Channel.GetMessageAsync(id);
if ( msg is IUserMessage message )
{
await message.ModifyAsync(m => m.Content = textMessage);
}
}
Taking into account what I have been told, I did tests I found this solution:
[Command("editmessage")]
public async Task EditMessage(string sMessageId, string TextMessage) {
ulong MessageId = Convert.ToUInt64(sMessageId);
var _Message = Context.Channel.GetMessageAsync(MessageId).Result; //It is important to put the `.Result`
var Message = _Message as IUserMessage;
await Message.ModifyAsync(msg => msg.Content = TextMessage);
}
I've been working on a discord bot for the past few days, and I managed to get one of the functions working: (a command that sets a message it's supposed to dm users when they join). I can't seem to get the bot to send the actual message.
private async Task Join(SocketGuildUser UID)
{
if (UID.IsBot || UID.IsWebhook) return;
Welcometxt= File.ReadAllText([FILE]);
await UID.SendMessageAsync("Your Message Was Sucessfully set!");
}
private async Task HandleCommandAsync(SocketMessage arg)
{
var message = arg as SocketUserMessage;
var context = new SocketCommandContext(_client, message);
if (message.Author.IsBot) return;
int argPos = 0;
if (message.HasStringPrefix("!", ref argPos))
{
var result = await _commands.ExecuteAsync(context, argPos, _services);
if (!result.IsSuccess) Console.WriteLine(result.ErrorReason);
}
}
When i check the logs it gives a null reference error with the var message and context, I tried googling the error and changing up the code but to no avail any advice? I believe the error is in one of these two methods but im not 100% positive
I guess you can use UserJoined event to achieve this.
Define event handler for UserJoined
public async Task UserJoined(SocketGuildUser user)
{
await user.SendMessageAsync("Hello");
}
Register it
private readonly DiscordSocketClient _client = new DiscordSocketClient();
private readonly CommandService _commandService = new CommandService();
public async Task MainAsync()
{
....
_client.UserJoined += UserJoined;
....
}
I'm not sure enough if this is related, but check if the Server Member Intents is on
My Azure function should listen Queue for messages, then get message, try to call an external service with value inside message, if the external service returns "OK" then we have to write message to another queue (for next Azure Function), if returns "Fail" we have to return to our current queue and retry by our Azure Function again after 5 minutes. How to implement it? I did it with Timer, but solution does not like me:
[FunctionName("FunctionOffice365VerificateDomain_and_AddService_and_GexMxRecord")]
public async static Task Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer,
[Queue("domain-verificate-Office365-add-services-get-mx-record", Connection = "StorageConnectionString")]CloudQueue listenQueue,
[Queue("domain-add-mx-record-to-registrator", Connection = "StorageConnectionString")]CloudQueue outputQueue,
ILogger log)
{
while (true)
{
// do "invisible" message for next 30 sec
var message = await listenQueue.GetMessageAsync();
if (message != null)
{
DomainForRegistration domainForRegistration = JsonConvert.DeserializeObject<DomainForRegistration>(message.AsString);
try
{
await _office365DomainService.VerifyDomainAsync(domainForRegistration.DomainName);
// remove message
await listenQueue.DeleteMessageAsync(message);
await _office365DomainService.UpdateIndicateSupportedServicesDomainAsync(domainForRegistration.DomainName);
var mxRecord = await _office365DomainService.GetMxRecordForDomainAsync(domainForRegistration.DomainName);
}
catch (DomainVerificationRecordNotFoundException)
{
// thrown when VerifyDomainAsync failed
}
}
else
break;
}
}
How to do it more carefully, without these while(true), but with timeout after fail validation?
Agree with #DavidG, try to use queue trigger to achieve your goal.
W can rely on the host setting of Queue.
visibilityTimeout is The time interval between retries when processing of a message fails
maxDequeueCount is The number of times to try processing a message before moving it to the poison queue.
{
"version": "2.0",
"extensions": {
"queues": {
"visibilityTimeout" : "00:05:00",
"maxDequeueCount": 2,
}
}
}
In this way, the function should look like
public static async Task Run(
[QueueTrigger("domain-verificate-Office365-add-services-get-mx-record")]string myQueueItem, ILogger log,
[Queue("domain-add-mx-record-to-registrator", Connection = "StorageConnectionString")]IAsyncCollector<string> outputQueue
)
{
// do stuff then output message
await outputQueue.AddAsync(myQueueItem);
}
If you don't want to throw the exception to host, we can turn to initialVisibilityDelay of CloudQueue method.
specifying the interval of time from now during which the message will be invisible
public static async Task Run(
[QueueTrigger("domain-verificate-Office365-add-services-get-mx-record")]string myQueueItem, ILogger log,
[Queue("domain-add-mx-record-to-registrator", Connection = "StorageConnectionString")]IAsyncCollector<string> outputQueue,
[Queue("domain-verificate-Office365-add-services-get-mx-record", Connection = "StorageConnectionString")]CloudQueue listenQueue
)
{
try
{
// do stuff then output message
await outputQueue.AddAsync(myQueueItem);
}
catch(DomainVerificationRecordNotFoundException)
{
// add the message in current queue and can only be visible after 5 minutes
await listenQueue.AddMessageAsync(new CloudQueueMessage(myQueueItem), null, TimeSpan.FromMinutes(5), null, null);
}
}
I want my Discord bot to greet members when they join a channel. I have been unable to find an event that fires when this happens. I have tried myClient.UserJoined += MyMethod; and others but they never get fired as I hope. Here is my main code:
public class Program
{
private DiscordSocketClient _client;
private CommandService _commands;
private IServiceProvider _services;
static void Main(string[] args)
=> new Program().RunBotAsync().GetAwaiter().GetResult();
public async Task RunBotAsync()
{
_client = new DiscordSocketClient();
_commands = new CommandService();
_services = new ServiceCollection()
.AddSingleton(_client)
.AddSingleton(_commands)
.BuildServiceProvider();
string botToken = // removed
_client.Log += Log;
await RegisterCommandsAsync();
await _client.LoginAsync(TokenType.Bot, botToken);
await _client.StartAsync();
await Task.Delay(-1);
}
private Task Log(LogMessage arg)
{
Console.WriteLine(arg);
return Task.CompletedTask;
}
public async Task RegisterCommandsAsync()
{
_client.MessageReceived += HandleCommandAsync;
_client.UserJoined += JoinedAsync; // Something like this to notify bot when someone has joined chat?
await _commands.AddModulesAsync(Assembly.GetEntryAssembly());
}
private Task JoinedAsync(SocketGuildUser arg)
{
throw new NotImplementedException();
}
private async Task HandleCommandAsync(SocketMessage arg)
{
var message = arg as SocketUserMessage;
if(message is null || message.Author.IsBot)
{
return;
}
int argPos = 0;
if (message.HasStringPrefix("!", ref argPos))
{
var context = new SocketCommandContext(_client, message);
await _commands.ExecuteAsync(context, argPos);
}
}
}
Thanks, and let me know if I can provide any more information.
Edit: The suggested link implements the UserJoined event, which only seems to trigger when a new member joins the channel. I need something that triggers everytime anyone logs in to the channel, even existing members.
Judging by the edit, I think you may have a slight mis conception of how the channels work.
Users join a guild, after which, they have become part of the guild.
After they join a guild, they are part of it, and the channels they are allowed to see. Hence there is no need to log into channels anymore.
Now what I think you want to achieve is sending a message in a channel / to a user whenever they switch from the offline state to the online state.
For this you could use the UserUpdated event. Where you can check the previous and the current status of a user, and send a message accordingly.
_client.UserUpdated += async (before, after) =>
{
// Check if the user was offline, and now no longer is
if(before.Status == UserStatus.Offline && after.Status != UserStatus.Offline)
{
// Find some channel to send the message to
var channel = e.Server.FindChannels("Hello-World", ChannelType.Text);
// Send the message you wish to send
await channel.SendMessage(after.Name + " has come online!");
}
}