I used this code to send a different message if a new user start conversation :
IConversationUpdateActivity update = message;
var client = new ConnectorClient(new Uri(message.ServiceUrl), new MicrosoftAppCredentials());
if (update.MembersAdded != null && update.MembersAdded.Any())
{
foreach (var newMember in update.MembersAdded)
{
if (newMember.Id != message.Recipient.Id)
{
var reply = message.CreateReply();
reply.Text = $"Welcome {newMember.Name}! You are a new member! If you want to see help menu , type : help";
client.Conversations.ReplyToActivityAsync(reply);
}
}
}
My problem is that when a user click in facebook : Get started this message comes twice.
Can you please help me ?
Facebook includes the conversation itself in the list of members:
So you need to change the if statement to this:
if (newMember.Id != message.Recipient.Id && newMember.Id != message.Conversation.Id)
{
// send welcome message
I'm looking into the facebook Name part...
You should be able to just plug in this code:
else if (message.Type == ActivityTypes.ConversationUpdate)
{
IConversationUpdateActivity iConversationUpdated = message as IConversationUpdateActivity;
if (iConversationUpdated != null)
{
ConnectorClient connector = new ConnectorClient(new System.Uri(message.ServiceUrl));
foreach (var member in iConversationUpdated.MembersAdded ?? System.Array.Empty<ChannelAccount>())
{
// if the bot is added, then
if (member.Id == iConversationUpdated.Recipient.Id)
{
var reply = ((Activity)iConversationUpdated).CreateReply($"Hi Friend I'm Botty McBotface");
connector.Conversations.ReplyToActivityAsync(reply);
}
}
}
}
Related
I am trying to create a discord bot using DSharpPlus library where if you react on a message with specific emoji, you will get a specific role. The concept is pretty straight forward but I fail to figure out one rather important concept. That is, how do I get the bot to listen for a reaction on an existing message all the time.
I tried to do it via commands and I got it to work, however the problem with this approach as I learned is that the bot only listens for reactions after I type a command and it only lasts a minute or so (based on configuration).
public class RoleCommands : BaseCommandModule
{
[Command("join")]
public async Task Join(CommandContext ctx)
{
var joinEmbed = new DiscordEmbedBuilder
{
Title = "Reaction with thumbs up!",
Color = DiscordColor.Green
};
var joinMessage = await ctx.Channel.SendMessageAsync(embed: joinEmbed).ConfigureAwait(false);
var thumbsUpEmoji = DiscordEmoji.FromName(ctx.Client, ":+1:");
var thumbsDownEmoji = DiscordEmoji.FromName(ctx.Client, ":-1:");
await joinMessage.CreateReactionAsync(thumbsUpEmoji).ConfigureAwait(false);
await joinMessage.CreateReactionAsync(thumbsDownEmoji).ConfigureAwait(false);
var interactivity = ctx.Client.GetInteractivity();
var reactionResult = await interactivity.WaitForReactionAsync(x =>
x.Message == joinMessage
&& x.User == ctx.User
&& x.Emoji == thumbsUpEmoji);
if (reactionResult.Result.Emoji == thumbsUpEmoji)
{
var role = ctx.Guild.GetRole(773965440913375282);
await ctx.Member.GrantRoleAsync(role).ConfigureAwait(false);
await joinMessage.DeleteAsync().ConfigureAwait(false);
}
}
}
How can I do this outside of a command where I can pass it a message Id and then it listens to that message for reactions all the time as oppose to a limited time?
The full answer to my question is to use DiscordClient.MessageReactionAdded += OnReactionAdded; and to implement the method as such:
private async Task OnReactionAdded(DiscordClient sender, MessageReactionRemoveEventArgs e)
{
var messageId = e.Message.Id;
var guild = e.Message.Channel.Guild;
var reactionName = e.Emoji.GetDiscordName();
var reactionDetail = ReactionDetails.FirstOrDefault(x =>
x.MessageId == messageId
&& x.GuildId == guild.Id
&& x.ReactionName == reactionName);
if (reactionDetail != null)
{
var member = e.User as DiscordMember;
if (member != null)
{
var role = guild.Roles.FirstOrDefault(x => x.Value.Id == reactionDetail.RoleId).Value;
await member.GrantRoleAsync(role).ConfigureAwait(false);
}
}
}
Store the message id somewhere then hook the MessageReactionAdded event on your DiscordClient and do your logic there.
I am trying to detect which message is edited or deleted on a subscribed channel on telegram with TLSharp library in c#.
1- in a while(true) loop I am getting latest updates.
2- when I delete or edit a message for test, I receive TLUpdateChannelTooLong only.
3- then I use client.GetHistoryAsync function to get channel messages, and check their EditDate.
But I don't know how much should I go deep in history and I can not find deleted message with this code easily.
Is there any solution to find deleted/edited messages easy and safe?
Part of my code:
state = await client.SendRequestAsync<TLState>(new TLRequestGetState());
while (true)
{
await Task.Delay(1000);
var req = new TLRequestGetDifference() { Date = state.Date, Pts = state.Pts, Qts = state.Qts };
TLDifference diff = null;
try
{
diff = await client.SendRequestAsync<TLAbsDifference>(req) as TLDifference;
}
catch (Exception ex)
{
HandleThisException(ex);
}
//--
if (diff != null)
{
state = await client.SendRequestAsync<TLState>(new TLRequestGetState());
foreach (var upd in diff.OtherUpdates.OfType<TLUpdateNewChannelMessage>())
{
var tm = (upd.Message as TLMessage);
if (tm == null) { continue; } // ?
var textMessage = tm.Message;
if (tm.Media != null)
{
if (tm.Media.GetType().ToString() == "TeleSharp.TL.TLMessageMediaPhoto")
{
var tLMessageMediaPhoto = (tm.Media as TLMessageMediaPhoto);
textMessage = tLMessageMediaPhoto.Caption;
}
}
try
{
var from = (tm.ToId as TLPeerChannel).ChannelId;
long replyTo = tm.ReplyToMsgId == null ? 0 : (long)tm.ReplyToMsgId;
await AnalyzeNewMessage( ... );
}
catch (Exception exParsing)
{
HandleThisException(exParsing);
}
}
// Checking Edited/Deleted Messages
foreach(var upLong in diff.OtherUpdates.OfType<TLUpdateChannelTooLong>())
{
TLChannel theChat = null;
foreach(var chat in diff.Chats.OfType<TLChannel>())
{
if(chat.Id == upLong.ChannelId) { theChat = chat; break; }
}
if (theChat != null)
{
var x = await client.GetHistoryAsync(
new TLInputPeerChannel { ChannelId = theChat.Id, AccessHash = (long)theChat.AccessHash },
0,-1,2
); // checking only 2 last messages!
var ChMsgs = x as TLChannelMessages;
foreach (var msg in ChMsgs.Messages.OfType<TLMessage>())
{
if(msg.EditDate != null)
{
var txt = msg.Message;
if (msg.Media != null)
{
if (msg.Media.GetType().ToString() == "TeleSharp.TL.TLMessageMediaPhoto")
{
txt = (msg.Media as TLMessageMediaPhoto).Caption;
}
}
await AnalyzeEditedMessage( ... );
}
}
}
}
}
}
I'm making a Discord bot and there's a channel in my server allocated to our rules and I want this bot to automatically send a message in that channel. Is it possible to check if the channel exists? Thanks.
Yes you definitely can.
if (message.Content.StartsWith("!check"))
{
SocketGuildChannel currentChannel = message.Channel as SocketGuildChannel;
SocketGuild guild = currentChannel.Guild;
foreach (SocketGuildChannel ch in guild.Channels)
{
if (ch.GetType() == typeof(SocketTextChannel)) //Checking text channels
{
if (ch.Name.Equals("rules"))
{
ISocketMessageChannel channel = (ISocketMessageChannel)ch; //Casting so we can send a message
await channel.SendMessageAsync("This is the rules channel.");
return;
}
}
}
await message.Channel.SendMessageAsync("Could not find the rules channel.");
return;
}
Assuming you are using Discord.Net 1.0.2
If you want to have it as a command:
[Command("check")]
public async Task CheckChannel(string channel)
{
foreach (SocketGuildChannel chan in Context.Guild.Channels)
{
if (channel == chan.Name)
{
// It exists!
ITextChannel ch = chan as ITextChannel;
await ch.SendMessageAsync("This is the rules channel!");
}
else
{
// It doesn't exist!
await ReplyAsync($"No channel named {channel} was found.");
}
}
}
You could also use Linq!
[Command("check")]
public async Task CheckChannel(string channelName)
{
//Makes the channel name NOT case sensitive
var channel = Context.Guild?.Channels.FirstOrDefault(c => string.Equals(c.Name, channelName, StringComparison.OrdinalIgnoreCase));
if (channel != null)
{
ITextChannel ch = channel as ITextChannel;
await ch.SendMessageAsync("This is the rules channel!");
}
else
{
// It doesn't exist!
await ReplyAsync($"No channel named {channel} was found.");
}
}
Also a warning, This will fail in a DM because Context.Guild == null in a direct message. If you so desired, you can add this snippet inside your command!
if (Context.IsPrivate)
{
await ReplyAsync("Cant call command from a direct message");
return;
}
Hi I created my first test bot using Microsoft BotFramework in C#.
in private async Task< Activity > HandleSystemMessage(Activity message) in if (message.Type == ActivityTypes.ConversationUpdate) normally it should notify a new member added to group or someone hit the start button of bot in Telegram Messenger. When I test it in debug mode using BotFramework emulator everything works perfectly but after I publish it I see that after hitting start button in Telegram messenger my code didn't run.
My code in ActivationType.ConversationUpdate
foreach (var item in message.MembersAdded)
{
try
{
using (var dbcontext = new WatermarkBotDBEntities())
{
dbcontext.BotUsers.Add(new BotUser()
{
AddedFriends = 0,
ConversationID = message.Conversation.Id,
ServiceUrl = message.ServiceUrl,
UserID = message.From.Id
});
dbcontext.SaveChanges();
if (Request.RequestUri.Query != "")
{
var u = dbcontext.BotUsers.Where(x => x.BotSalCode == Request.RequestUri.Query.Replace("?start=", string.Empty)).FirstOrDefault();
u.AddedFriends++;
dbcontext.Entry(u).State = System.Data.Entity.EntityState.Modified;
if (u != null)
{
var connector = new ConnectorClient(new Uri(u.ServiceUrl));
IMessageActivity newMessage = Activity.CreateMessageActivity();
newMessage.Type = ActivityTypes.Message;
//newMessage.From = new ChannelAccount("<BotId>", "<BotName>");
newMessage.From = new ChannelAccount("c3e7mhdafcecn7ng3", "Bot");
newMessage.Conversation = new ConversationAccount(false, u.ConversationID);
newMessage.Recipient = new ChannelAccount(u.UserID);
if (u.AddedFriends <= 2)
newMessage.Text = $"SomeText.";
else newMessage.Text = "SomeTex";
await connector.Conversations.SendToConversationAsync((Activity)newMessage);
dbcontext.SaveChanges();
}
}
}
}
catch (Exception ex)
{
}
So how is it possible to detect hitting start in telegram ?
Regards
I realize this is not a complete answer, but I wanted to share this code with you in case it may help. Below is the recommended way to send a welcome message, you may be able to repurpose this code for your use.
else if (message.Type == ActivityTypes.ConversationUpdate || message.Type == ActivityTypes.Message)
{
IConversationUpdateActivity iConversationUpdated = message as IConversationUpdateActivity;
if (iConversationUpdated != null)
{
ConnectorClient connector = new ConnectorClient(new System.Uri(message.ServiceUrl));
foreach (var member in iConversationUpdated.MembersAdded ?? System.Array.Empty<ChannelAccount>())
{
// if the bot is added, then
if (member.Id == iConversationUpdated.Recipient.Id)
{
var reply = ((Activity)iConversationUpdated).CreateReply(
$"Hi! I'm Botty McBot.");
await connector.Conversations.ReplyToActivityAsync(reply);
}
}
}
}
This is the answer I found for my question after lots of testing :
In MessagesController class in public async Task<HttpResponseMessage> Post([FromBody]Activity activity) function that defined by default in a BotFramework Application you have to do something like this :
if (activity.Type == ActivityTypes.Message)
{
if (activity.Text.StartsWith("/start"))
{
//This will return you the start parameter of a link like : http://telegram.me/botname?start=Parameter
var Parameter = activity.Text.Replace("/start ", "");
}
}
and if you want to send a welcome message so you can surely use the way that #JasonSowers told and use his code to send your message .
Best Regards
I am using the following code to add user in group and save user in db against this particular group using the following code.
SERVER:
public class ChatHub : Hub
{
public async Task JoinRoom(string user_Id, string room_Id, string user_Name)
{
AddLoginUser(room_Id, this.Context.ConnectionId, user_Id);
await this.Groups.Add(this.Context.ConnectionId, room_Id);
}
public void Connect(string user_Id, string room_Id, string user_Name)
{
var id = Context.ConnectionId;
Clients.Caller.onConnected(id, user_Name, GetRoomUser(room_Id), GetRoomMessage(room_Id));
// send to all in group to update user list
Clients.OthersInGroup(room_Id).onNewUserConnected(id, user_Name);
}
public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
{
using (DataContext dc = new DataContext())
{
var item = dc.LoggedInUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
if (item != null)
{
item.Connected = false;
dc.SubmitChanges();
Clients.OthersInGroup(item.RoomID.ToString()).onUserDisconnected(Context.ConnectionId, item.UserMaster.User_Name);
}
return base.OnDisconnected(stopCalled);
}
}
}
private void AddLoginUser(string room_Id, string connection_Id, string user_Id)
{
using (DataContext dc = new DataContext())
{
var checkUserLogedIn = (from user in dc.LoggedInUsers
where (user.RoomID == Convert.ToInt32(room_Id) && user.UserID == Convert.ToInt32(user_Id))
select user).SingleOrDefault();
if (checkUserLogedIn == null)
{
LoggedInUser objLoggedInUser = new LoggedInUser();
objLoggedInUser.ConnectionId = connection_Id;
objLoggedInUser.UserID = Convert.ToInt32(user_Id);
objLoggedInUser.RoomID = Convert.ToInt32(room_Id);
objLoggedInUser.Connected = true;
dc.LoggedInUsers.InsertOnSubmit(objLoggedInUser);
dc.SubmitChanges();
}
else
{
if (!checkUserLogedIn.Connected)
{
checkUserLogedIn.Connected = true;
dc.SubmitChanges();
}
}
}
}
Problem:
Suppose i logged-in with userid=1 for roomid=1 and contextid=123asd. If i refresh my window then contextid will change and now if i closing browser tab then following query:
var item = dc.LoggedInUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
not find out the user against latest connectionid, because when i had saved user on connect at that time connectionid was different.
How i can set connected status false for particular user on disconnect event.
Thanks in advance.
OnConnected you should save all connectionIds (which is mapped with user), connectionId should be unique, not the user. Because a user can have more than one connection to signalr at the same time(New Tabs).
Everytime you should map user and connectionId on Onconnected. Everytime you should just remove that connectionId, not all connectionIds of user on OnDisconnected. You should add connectionId with user if it's not in list(if stop called is not called disconnected can occur even user is not disconnected) on OnReconnected.
You should refactor your code base on this. First, you should remove connectionId. Then, you can check; if there is no record left with this user(which is mapped with that connectionId) on list, you can send message.
Check here
I have changed your code a bit, you can improve this code based on this knowledge. You should call AddLoginUser on OnReconnected also.
public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
{
using (DataContext dc = new DataContext())
{
var item = dc.LoggedInUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
if (item != null)
{
dc.LoggedInUsers.Remove(item);
dc.SubmitChanges();
//If there is no other connection left with this user in this room send message.
if (!dc.LoggedInUsers.Any(x => x.RoomID==item.RoomID && x.userId==item.UserId)
Clients.OthersInGrouproomId.ToString()).onUserDisconnected(Context.ConnectionId, item.UserMaster.User_Name);
}
return base.OnDisconnected(stopCalled);
}
}
}
private void AddLoginUser(string room_Id, string connection_Id, string user_Id)
{
using (DataContext dc = new DataContext())
{
//Just check connectionId uniqunes. You don't need connected field.
var checkUserLogedIn = (from user in dc.LoggedInUsers
where user.ConnectionId == connection_Id
select user).SingleOrDefault();
if (checkUserLogedIn == null)
{
LoggedInUser objLoggedInUser = new LoggedInUser();
objLoggedInUser.ConnectionId = connection_Id;
objLoggedInUser.UserID = Convert.ToInt32(user_Id);
objLoggedInUser.RoomID = Convert.ToInt32(room_Id);
dc.LoggedInUsers.InsertOnSubmit(objLoggedInUser);
dc.SubmitChanges();
}
}
}