Unable to make bot reply in thread on Slack - c#

I have a single-tenant bot, built on Bot.Builder 18.1 that is currently running in a Slack channel. My challenge is that I would like the bot to always reply in a thread, when possible, so:
If already in a thread, reply in that thread
If on a channel, create a new thread and reply there
As far as I understand, the way to achieve this is to set the property thread_ts to the value of the ts property. I can get this value, however all attempts to set thread_ts still only give me replies in the channel directly, and not in a new thread, as expected. My last failed attempt was setting it on the reply message:
string? thread_ts = null;
if (slackChannelData is not null)
{
thread_ts = slackChannelData?.SlackMessage?._event?.ts;
}
reply.Conversation.Properties[nameof(thread_ts)] = thread_ts;
And yes, I do get the ts value out of that :)
I also attempted to use the Slack Adapter, however it fails for som authentication reasons, even though I've set it's required configuration values (SlackBotToken, SlackVerificationToken and SlackClientSigningSecret). I cannot get the Slack Adapter to get past the authentication. The slack adapter follows the latest example on GitHub, and targets the community.adapters.slack version 4.13.5
So my question is:
Does anyone know if it is possible to use the Bot Framework and a slack channel to reply in thread in Slack given my want above?

Related

Discord.NET Trying to get SocketUser as SocketGuildUser returning null

I am working on a C# Discord.Net bot, and have come across an issue I thought I had previously resolved.
I need to get the user mentioned in the message as a 'SocketGuildUser'. This works perfectly fine when I attempt it on my own user. However, it wasn't working on my friend's user. While it could get a 'SocketUnknownUser', it failed to get a 'SocketGuildUser', just returning null. After I removed a certain role from them, the bot worked completely fine for a couple of days. However, all of sudden, it has now stopped being able to get a 'SocketGuildUser' from my friend's user, with no changes to the relevant code.
Here is the relevant code:
var userToModCheck = message.MentionedUsers.ElementAt(0);
var userToMod = message.MentionedUsers.ElementAt(0) as SocketGuildUser;
userToModCheck returns the correct username and user ID, but as a SocketUnknownUser. userToMod returns null.
So far, I have tried:
Checking the bot 'Privileged Gateway Intents' permissions in the Discord developer portal
Using a separate function(Context.Guild.GetUser) to get the SocketGuildUser using the user ID
Changing roles
Restarting both computers
Searching the internet
Edit: There are no issues when the mentioned user is online and has the online status. Very strange.
Edit 2: Switching 'AlwaysDownloadUsers' to true does not resolve the issue.
'DiscordSocketConfig' wasn't configured properly. Firstly, make sure all 'Priveleged Gateway Intents' are enabled in the Discord Developer Portal. (Application > Bot).
Change 'AlwaysDownloadUsers' to true, and 'GatewayIntents' to all, then apply it to the client:
var config = new DiscordSocketConfig{ AlwaysDownloadUsers = true, GatewatIntents = GatewayIntents.All };
_client = new DiscordSocketClient{config};
The bot then works properly.

Discord.NET Guild.DefaultChannel does not work

I am coding a discord bot, and I want it to send a message to a server default channel whenever the bot joins a new server.
Here is my code
client.JoinedGuild += async guild =>
{
var channel = guild.DefaultChannel;
await channel.SendMessageAsync("test");
};
The error occurs on channel.SendMessageAsync, and when I debugged the program, it keeps showing that channel is null even though I do have a default channel in my server.
A discord update months ago removed defaultChannel property. Which means that servers now do not need to have a default channel at all.
(You can even delete all channel in your server!)
Which also means Guild.DefaultChannel property will not work as intended anymore.
(Correct me if I am wrong, but if a server still has a #general channel, the property will work as intended.)
Discord.NET v2+ have a working DefaultChannel property. (It uses its own set of algorithm to determine which would be the default channel for the guild.)
The source code for the algorithm is here. (Line 66-69)
Looking at the algorithm, you may want to be slightly cautious about using it, if your bot have permission to send messages everywhere, the DefaultChannel property would simply be the first channel in the server's channel list.
(And it would be bad news if that was a readme channel)

Difficulty accessing messages in an existing conversation in Microsoft Bot Framework

I'm trying to build a separate app - basically a daemon running on my intranet - that can connect to a Bot Connector app and access an existing conversation between a user and the bot.
To start at this, I'm using the DirectLine NuGet package, and then searching for existing messages with the conversationId found in a conversation I've started with the bot via the emulator.
For example, I fired up the emulator, and got the conversationId "8a684db8". Over in my console app, I've got the following relevant code:
var uri = new Uri("https://directline.botframework.com");
DirectLineClientCredentials creds = new DirectLineClientCredentials(secret); //lot into the bot framework
DirectLineClient client = new DirectLineClient(uri, creds); //connect the client
Conversations convs = new Conversations(client); //get the list of conversations belonging to the bot? Or does this start a new collection of conversations?
var existing_conv_messages = convs.GetMessages(convo_id);
Now, when I try to run it, I'm getting a "Not Found" error on the last line where I tried to find existing messages.
Anyone have a solution here?
Conversations sent to the emulator don't get sent to the Direct Line API. For messages to be viewable in Direct Line, they need to be sent through the online, running Connector and Direct Line services.

Converting Microsoft EWS StreamingNotification Example to a service

I've been working to try and convert Microsoft's EWS Streaming Notification Example to a service
( MS source http://www.microsoft.com/en-us/download/details.aspx?id=27154).
I tested it as a console app. I then used a generic service template and got it to the point it would compile, install, and start. It stops after about 10 seconds with the ubiquitous "the service on local computer started and then stopped."
So I went back in and upgraded to C# 2013 express and used NLog to put a bunch of log trace commands to so I could see where it was when it exited.
The last place I can find it is in the example code, SynchronizationChanges function,
public static void SynchronizeChanges(FolderId folderId)
{
logger.Trace("Entering SynchronizeChanges");
bool moreChangesAvailable;
do
{
logger.Trace("Synchronizing changes...");
//Console.WriteLine("Synchronizing changes...");
// Get all changes since the last call. The synchronization cookie is stored in the
// _SynchronizationState field.
// Only the the ids are requested. Additional properties should be fetched via GetItem
//calls.
logger.Trace("Getting changes into var changes.");
var changes = _ExchangeService.SyncFolderItems(folderId, PropertySet.IdOnly, null, 512,
SyncFolderItemsScope.NormalItems,
_SynchronizationState);
// Update the synchronization cookie
logger.Trace("Updating _SynchronizationState");
the log file shows the trace message ""Getting changes into var changes." but not the "Updating _SynchronizationState" message.
so it never gets past var changes = _ExchangeService.SyncFolderItems
I cannot for the life figure out why its just exiting. There are many examples of EWS streaming notifications. I have 3 that compile and run just fine but nobody as far as I can tell has posted an example of it done as a service.
If you don't see the "Updating..." message it's likely the sync threw an exception. Wrap it in a try/catch.
OK, so now that I see the error, this looks like your garden-variety permissions problem. When you ran this as a console app, you likely presented the default credentials to Exchange, which were for your login ID. For a Windows service, if you're running the service with one of the built-in accounts (e.g. Local System), your default credentials will not have access to Exchange.
To rectify, either (1) run the service under the account you did the console app with, or (2) add those credentials to the Exchange Service object.

Push Notifications with PushSharp - the basics

I need to push notifications to tens of thousands of iOS devices that my app installed. I'm trying to do it with PushSharp, but I'm missing some fundamental concepts here. At first I tried to actually run this in a Windows service, but couldn't get it work - getting null reference errors coming from _push.QueueNotification() call. Then I did exactly what the documented sample code did and it worked:
PushService _push = new PushService();
_push.Events.OnNotificationSendFailure += new ChannelEvents.NotificationSendFailureDelegate(Events_OnNotificationSendFailure);
_push.Events.OnNotificationSent += new ChannelEvents.NotificationSentDelegate(Events_OnNotificationSent);
var cert = File.ReadAllBytes(HttpContext.Current.Server.MapPath("..pathtokeyfile.p12"));
_push.StartApplePushService(new ApplePushChannelSettings(false, cert, "certpwd"));
AppleNotification notification = NotificationFactory.Apple()
.ForDeviceToken(deviceToken)
.WithAlert(message)
.WithSound("default")
.WithBadge(badge);
_push.QueueNotification(notification);
_push.StopAllServices(true);
Issue #1:
This works perfectly and I see the notification pop up on the iPhone. However, since it's called a Push Service, I assumed it would behave like a service - meaning, I instantiate it and call _push.StartApplePushService() within a Windows service perhaps. And I thought to actually queue up my notifications, I could do this on the front-end (admin app, let's say):
PushService push = new PushService();
AppleNotification notification = NotificationFactory.Apple()
.ForDeviceToken(deviceToken)
.WithAlert(message)
.WithSound("default")
.WithBadge(badge);
push.QueueNotification(notification);
Obviously (and like I already said), it didn't work - the last line kept throwing a null reference exception.
I'm having trouble finding any other kind of documentation that would show how to set this up in a service/client manner (and not just call everything at once). Is it possible or am I missing the point of how PushSharp should be utilized?
Issue #2:
Also, I can't seem to find a way to target many device tokens at once, without looping through them and queuing up notifications one at a time. Is that the only way or am I missing something here as well?
Thanks in advance.
#baramuse explained it all, if you wish to see a service "processor" you can browse through my solution on https://github.com/vmandic/DevUG-PushSharp where I've implemented the workflow you seek for, i.e. a win service, win processor or even a web api ad hoc processor using the same core processor.
From what I've read and how I'm using it, the 'Service' keyword may have mislead you...
It is a service in a way that you configure it once and start it.
From this point, it will wait for you to push new notifications inside its queue system and it will raise events as soon as something happens (delivery report, delivery error...). It is asynchronous and you can push (=queue) 10000 notifications and wait for the results to come back later using the event handlers.
But still it's a regular object instance you will have to create and access as a regular one. It doesn't expose any "outside listener" (http/tcp/ipc connection for example), you will have to build that.
In my project I created a small selfhosted webservice (relying on ServiceStack) that takes care about the configuration and instance lifetime while only exposing the SendNotification function.
And about the Issue #2, there indeed isn't any "batch queue" but as the queue function returns straight away (enqueue and push later) it's just a matter of a looping into your device tokens list...
public void QueueNotification(Notification notification)
{
if (this.cancelTokenSource.IsCancellationRequested)
{
Events.RaiseChannelException(new ObjectDisposedException("Service", "Service has already been signaled to stop"), this.Platform, notification);
return;
}
notification.EnqueuedTimestamp = DateTime.UtcNow;
queuedNotifications.Enqueue(notification);
}

Categories