Discord Bot ( using C# ) does not execute the command - c#

I wrote a Discord Bot. It's developed with C#. My command list is filled, the command value receives this list. But the command does not execute the code when calling it.
My prefix char is '!' followed by the command. My base class looks this:
public class Bot
{
string token = "#######################"; // my Token
CommandService command; // The commands holder
EventController eventController = new EventController(); // event class
CommandController commandController = new CommandController(); // commands class
public Bot()
{
var client = new DiscordClient(); // my client
client = new DiscordClient(input =>
{
input.LogLevel = LogSeverity.Info;
input.LogHandler = Log;
});
client.UsingCommands(input =>
{
input.PrefixChar = '!'; // the prefix char to call commands
input.AllowMentionPrefix = true;
});
eventController.HandleEvents(client); // reference to events
command = client.GetService<CommandService>();
commandController.HandleCommands(command, client); // reference to commands
client.ExecuteAndWait(async() =>
{
while (true)
{
await client.Connect(token, TokenType.Bot);
break;
}
});
}
private void Log(object sender, LogMessageEventArgs e)
{
Console.WriteLine(e.Message);
}
I devided the code into two classes, the eventController and the commandController. The commandController is the relevant one.
My command class looks this:
private List<Tuple<string, string, string>> commandList = new List<Tuple<string, string, string>>(); // the List holding all commands
public void HandleCommands(CommandService command, DiscordClient client)
{
FillCommandList(); // Fill the List with the commands
foreach (Tuple<string, string, string> tuple in commandList)
{
command.CreateCommand('!' + tuple.Item1).Do(async (e) =>
{
await e.Channel.SendMessage(tuple.Item2); // Create all commands from the List
});
}
}
private void Add(string commandName, string textToReturn, string commandDescription)
{
commandList.Add(new Tuple<string, string, string>(commandName, textToReturn, commandDescription)); // Method to lower the mass of code
}
private void FillCommandList()
{
Add("test0", "success0", "info0"); // commands for testing
Add("test1", "success1", "info1");
Add("test2", "success2", "info2");
Add("test3", "success3", "info3");
Add("help", UseHelp(), "List all Commands"); // call the help
}
private string UseHelp()
{
string commandItems = "";
foreach (Tuple<string, string, string> tuple in commandList)
{
commandItems += "- !" + tuple.Item1 + " - " + tuple.Item3 + "\r\n"; // List all commands
}
return commandItems;
}
So when I call a command like "test0" or "UseHelp()" the command receives the string content. All 5 commands are listed to the bot. But when i use a command in Discord the Bot does not reply.
It is connected and the "command" data is filled...

First, take a look at this :
client.UsingCommands(input =>
{
input.PrefixChar = '!'; // the prefix char to call commands
input.AllowMentionPrefix = true;
});
and now , this :
command.CreateCommand('!' + tuple.Item1)
In discord.net , when you make a PrefixChar already, the PrefixChar will always appear inside the argument of command.CreateCommand() at the front by default. So hence there is no need to place another '!' inside. If you do that, you have to call a command by using !!test0 . Simply, treat it as the system has automatically added the prefix in the argument on command.CreateCommand() automatically at the front.
To fix it : simply remove char argument '!' at the front in command.CreateCommand('!' + tuple.Item1). Test the bot by calling !test0 or something, it should work.

Related

Detect if method called via delegate discards argument

I'm making a command system for my console app. Some commands are used "as-is" (like "exitapp"), some require arguments (like "server start ip port"). Here's how I am doing it:
delegate void CommandHandlerMethod(params object[] args);
readonly Dictionary<string, CommandHandlerMethod> commands;
For methods which doesn't require arguments, i use discarding feature like this:
void ExitAppCommand(params object[] _) { ... }
void StartServerCommand(params object[] args)
{
// ip = args[0];
// port = args[1];
}
Then I just loop through dictionary, and I want to decide between two situations:
1. if command-handling method discard arguments then just call it
2. otherwise cut arguments from input string and pass it as args
So what is the option to detect argument discard?
UDP: This is "pseudo" of what I exactly want:
foreach (KeyValuePair<string, CommandHandlerMethod> cmd in commands)
{
(input.StartsWith(cmd.Key))
{
if (cmd.Value. /*is declared to discard arg */)
cmd.Value();
else
// else cut args and pass them
cmd.Value(input.Substring(cmd.Key.Length+1).Split(' '));
break;
}
}
The _ in void ExitAppCommand(params object[] _) { ... } is not a discard. _ is a valid C# identifier.
In the following code, however, _ is a discard:
public void N(int x)
{
_ = x.ToString();
_ = 2 + 3;
}
Chexk it out here.

Discord API - How do I use SocketCommandContext or CommandService.ExecuteAsync to pass additional parameters?

I'm creating a discord bot through discord's open source library. Here is how all of my commands get created:
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) || message.HasMentionPrefix(client.CurrentUser, ref argPos))
{
var context = new SocketCommandContext(client, message);
var result = await commands.ExecuteAsync(context, argPos, services);
if(!result.IsSuccess)
Console.WriteLine(result.ErrorReason);
}
}
And here is my problem command:
public class AddArchiveCommand : ModuleBase<SocketCommandContext>
{
DiscordSocketClient client;
SocketUserMessage msg;
List<Archive> archiveList;
public AddArchiveCommand(DiscordSocketClient client, SocketUserMessage msg, List<Archive> archiveList)
{
this.archiveList = archiveList;
}
[Command("addarchive")]
public async Task AddArchiveAsync(ITextChannel sourceChan, ITextChannel destChan)
{
Archive archive = new Archive(sourceChan.Id, destChan.Id, archiveList);
archive.AddArchive(); //Using custom method rather than the list add because it check for more logic.
await ReplyAsync("Now archiving " + sourceChan.Name + " to " + destChan.Name);
}
}
I'm trying to pass the List archiveList variable through a constructor from my main class into the command in order to add an archive request to this list. Problem is, I'm not able to do something as simple as "var example = new AddArchiveCommand(archiveList)" due to the nature of how the SocketCommandContext class works.
What am I missing here? Thank you for any explanation. I've been stuck on this for weeks and just need to work with someone on a solution.

Call Monitor using C# TAPI

I need to create a program to monitor activity of phone call.And get information about the calls, like number and Name.
I'm not strong with TAPI code and C#, so hope that anybody can help me, I'm desperate.
I have this code where I try to detect the available devices and obtain information from those devices when a call comes in:
using System;
using TAPI3Lib;
using JulMar.Atapi;
namespace ConsoleApp1
{
class Program
{
private void tapi_ITTAPIEventNotification_Event(TAPI_EVENT TapiEvent, object pEvent)
{
try
{
ITCallNotificationEvent cn = pEvent as ITCallNotificationEvent;
if(cn.Call.CallState == CALL_STATE.CS_CONNECTED)
{
string calledidname = cn.Call.get_CallInfoString(CALLINFO_STRING.CIS_CALLEDIDNAME);
Console.WriteLine("Called ID Name " + calledidname);
string callednumber = cn.Call.get_CallInfoString(CALLINFO_STRING.CIS_CALLEDIDNUMBER);
Console.WriteLine("Called Number " + callednumber);
string calleridname = cn.Call.get_CallInfoString(CALLINFO_STRING.CIS_CALLERIDNAME);
Console.WriteLine("Caller ID Name " + calleridname);
string callernumber = cn.Call.get_CallInfoString(CALLINFO_STRING.CIS_CALLERIDNUMBER);
Console.WriteLine("Caller Number " + callernumber);
}
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
static void Main(string[] args)
{
TapiManager mgr = new TapiManager("ConsoleApp1");
mgr.Initialize();
foreach(TapiLine line in mgr.Lines)
{
foreach (string s in line.Capabilities.AvailableDeviceClasses)
Console.WriteLine("{0} - {1}", line.Name, s);
}
}
}
}
But when I run it, just see the available devices but don't see any information about calls. I'm used to programming in java so I guess I should send to call the method that gets the call information in the main, but I do not know how to do that and in any code I've seen they do.
So, hope that you can help me to understand how TAPI works and what can I do to do my code work.
Okay, first, you want to stick to one Version of TAPI. In your using statements, youre importing a TAPI 2.x managed libaray and a TAPI 3.x managed library.
using TAPI3Lib; // this is a TAPI 3.x library
using JulMar.Atapi; // this is a TAPI 2.x library
If you're choosing to go with TAPI 3.x, you should start by creating a new class, which handles different kinds of TAPI events. To do so, it needs to implement the ITTAPIEventNotification interface:
public class CallNotification : ITTAPIEventNotification
{
public void Event(TAPI_EVENT TapiEvent, object pEvent)
{
if(pEvent == null)
throw new ArgumentNullException(nameof(pEvent));
switch (TapiEvent)
{
case TAPI_EVENT.TE_CALLNOTIFICATION:
// This event will be raised every time a new call is created on an monitored line-
// You can use CALLINFO_LONG.CIL_ORIGIN to see weather it's an inbound call, or an
// outbound call.
break;
case TAPI_EVENT.TE_CALLSTATE:
// This event will be raised every time the state of a call on one of your monitored
// Lines changes.
// If you'd want to read information about a call, you can do it here:
ITCallStateEvent callStateEvent = (ITCallStateEvent)pEvent;
ITCallInfo call = callStateEvent.Call;
string calledidname = call.get_CallInfoString(CALLINFO_STRING.CIS_CALLEDIDNAME);
Console.WriteLine("Called ID Name " + calledidname);
string callednumber = call.get_CallInfoString(CALLINFO_STRING.CIS_CALLEDIDNUMBER);
Console.WriteLine("Called Number " + callednumber);
string calleridname = call.get_CallInfoString(CALLINFO_STRING.CIS_CALLERIDNAME);
Console.WriteLine("Caller ID Name " + calleridname);
string callernumber = call.get_CallInfoString(CALLINFO_STRING.CIS_CALLERIDNUMBER);
Console.WriteLine("Caller Number " + callernumber);
break;
}
// Since you're working with COM objects, you should release any used references.
Marshal.ReleaseComObject(pEvent);
}
}
In order to use this class, you need to create a new instance of TAPI3Lib.TAPIClass and call its Initialize method. After that, you can attach your newly create CallNotification class as an event handler. You could also specify which types of events you want your handler to receive. Notice that you wont receive any event notifications at this point, because you haven't told TAPIClass which lines it should monitor:
CallNotification callevent = new CallNotification();
TAPIClass tapi = new TAPIClass();
tapi.Initialize();
tapi.EventFilter = (int)(TAPI_EVENT.TE_CALLNOTIFICATION | TAPI_EVENT.TE_CALLSTATE);
tapi.ITTAPIEventNotification_Event_Event += new ITTAPIEventNotification_EventEventHandler(callevent.Event);
In order to tell TAPIClass which lines it should monitor, you need to do two things. ask for all lines registered to you IPBX and determine, weather its a line you have the rights to monitor (this is an IPBX configuration):
public List<ITAddress> EnumerateLines(TAPIClass tapi)
{
List<ITAddress> addresses = new List<ITAddress>();
ITAddress address;
uint arg = 0;
ITAddressCapabilities addressCapabilities;
int callfeatures;
int linefeatures;
bool hasCallFeaturesDial;
bool hasLineFeaturesMakeCall;
IEnumAddress ea = tapi.EnumerateAddresses();
do
{
ea.Next(1, out address, ref arg);
if (address != null)
{
addressCapabilities = (ITAddressCapabilities)address;
callfeatures = addressCapabilities.get_AddressCapability(ADDRESS_CAPABILITY.AC_CALLFEATURES1);
linefeatures = addressCapabilities.get_AddressCapability(ADDRESS_CAPABILITY.AC_LINEFEATURES);
hasCallFeaturesDial = (callfeatures1 & (int)0x00000040) != 0; //Contains LineCallFeatures Dial; see Tapi.h for details
hasLineFeaturesMakeCall = (linefeatures & (int)0x00000008) != 0; //Contains LineFeatures MakeCall; see Tapi.h for details
// this is basically saying "Am I allowed to dial numbers and create calls on this specific line?"
if(hasCallFeaturesDial && hasLineFeaturesMakeCall)
addresses.Add(address);
}
} while (address != null);
return addresses;
}
public void RegisterLines(TAPIClass tapi, IEnumerable<ITAddress> addresses)
{
if (tapi == null)
throw new ArgumentNullException(nameof(tapi));
if (addresses == null)
throw new ArgumentNullException(nameof(addresses));
foreach (ITAddress address in addresses)
{
tapi.RegisterCallNotifications(address, true, true, TapiConstants.TAPIMEDIATYPE_AUDIO, 2);
}
}
so Your initialization would look like this:
CallNotification callevent = new CallNotification();
TAPIClass tapi = new TAPIClass();
tapi.Initialize();
IEnumerable<ITAddress> addresses = this.EnumerateLines(tapi);
this.RegisterLines(tapi, addresses);
tapi.EventFilter = (int)(TAPI_EVENT.TE_CALLNOTIFICATION | TAPI_EVENT.TE_CALLSTATE);
tapi.ITTAPIEventNotification_Event_Event += callevent.Event;
Once you run your program, and it's done executing above code, you will get notifications from incoming and outgoing calls when their call state changes.
I hope you could follow this post. If you have any questions, just ask =)
The line is TapiLine, you have to use TapiCall.

Looking for a C# URL router, but not for HTTP

I am looking for a C# URL router component. Something very classic, taking inputs string such as /resources/:id/stuff and calling the appropriate methods. Something similar to the default ASP.net routing or RestfullRouting.
However, I am not using HTTP and I don't want a full HTTP stack/framework. I am looking for something light to route my MQTT messages.
Do you know if such a component exist?
The following non-optimized, not really defensively coded code parses URIs against routes:
public class UriRouteParser
{
private readonly string[] _routes;
public UriRouteParser(IEnumerable<string> routes)
{
_routes = routes.ToArray();
}
public Dictionary<string, string> GetRouteValues(string uri)
{
foreach (var route in _routes)
{
// Build RegEx from route (:foo to named group (?<foo>[a-z0-9]+)).
var routeFormat = new Regex("(:([a-z]+))\\b").Replace(route, "(?<$2>[a-z0-9]+)");
// Match uri parameter to that regex.
var routeRegEx = new Regex(routeFormat);
var match = routeRegEx.Match(uri);
if (!match.Success)
{
continue;
}
// Obtain named groups.
var result = routeRegEx.GetGroupNames().Skip(1) // Skip the "0" group
.Where(g => match.Groups[g].Success && match.Groups[g].Captures.Count > 0)
.ToDictionary(groupName => groupName, groupName => match.Groups[groupName].Value);
return result;
}
// No match found
return null;
}
}
It makes a few assumptions about the input (both routes and URIs), but basically it picks the :foo parameter names from the routes and makes named capture groups from that, which are matched against the input URI.
To be used like this:
var parser = new UriRouteParser(new []{ "/resources/:id/stuff" });
var routeValues = parser.GetRouteValues("/resources/42/stuff");
This will yield a dictionary of { "id" = "42" }, which you can then use as you like.
I quickly changed #CodeCaster's solution to attach and invoke delegates.
public class UriRouter
{
// Delegate with a context object and the route parameters as parameters
public delegate void MethodDelegate(object context, Dictionary<string, string> parameters);
// Internal class storage for route definitions
protected class RouteDefinition
{
public MethodDelegate Method;
public string RoutePath;
public Regex RouteRegEx;
public RouteDefinition(string route, MethodDelegate method)
{
RoutePath = route;
Method = method;
// Build RegEx from route (:foo to named group (?<foo>[a-z0-9]+)).
var routeFormat = new Regex("(:([a-z]+))\\b").Replace(route, "(?<$2>[a-z0-9]+)");
// Build the match uri parameter to that regex.
RouteRegEx = new Regex(routeFormat);
}
}
private readonly List<RouteDefinition> _routes;
public UriRouter()
{
_routes = new List<RouteDefinition>();
}
public void DefineRoute(string route, MethodDelegate method)
{
_routes.Add(new RouteDefinition(route, method));
}
public void Route(string uri, object context)
{
foreach (var route in _routes)
{
// Execute the regex to check whether the uri correspond to the route
var match = route.RouteRegEx.Match(uri);
if (!match.Success)
{
continue;
}
// Obtain named groups.
var result = route.RouteRegEx.GetGroupNames().Skip(1) // Skip the "0" group
.Where(g => match.Groups[g].Success && match.Groups[g].Captures.Count > 0)
.ToDictionary(groupName => groupName, groupName => match.Groups[groupName].Value);
// Invoke the method
route.Method.Invoke(context, result);
// Only the first match is executed
return;
}
// No match found
throw new Exception("No match found");
}
}
That can be used like this:
var router = new UriRouter();
router.DefineRoute("/resources/:id/stuff",
(context, parameters) => Console.WriteLine(parameters["id"] + " - " + context));
router.Route("/resources/42/stuff", "abcd");
That will print 42 - abcd on the standard output.

Making part of a split string public, and the other part local

Apologies about the (possibly) basic question, but I am attempting to split a string, and making one part of the string public, so I can call it later in the form in a different class.
It is a simple Skype chat bot, and it reads the message sent to me for processing. However, I am attempting to make it so that if someone sent a command with two words - e.g. !command name - the !command command will be processed, and later in the form, I will use the second part of the split string to be able to process it. Here is what I am attempting -
The splitting and reading of the message -
public void skype_MessageStatus(ChatMessage msg, TChatMessageStatus status)
{
if (msg.Body.IndexOf(trigger) == 0 && TChatMessageStatus.cmsReceived == status)
{
string command = msg.Body.Remove(0, trigger.Length).ToLower();
var splitted = command.Split(' ');
string command1 = splitted[0];
string name = splitted[1];
msg.Chat.SendMessage(nick + ProcessCommand(command1));
}
}
There are several other commands in this chat bot, so there is a switch containing different outcomes - as for !command, I have -
case "command":
result = command();
break;
And finally -
private string command()
{
WebRequest.Create("API I have" + name);
new WebClient().DownloadString("API I have" + name);
}
I would like to be able to use 'name' here, from the split message. Thanks, and any help is appreciated.
First, define the pattern you want to use to parse your inbound messages. It seems like you have multiple commands that probably have different parameters, but all commands take the following form:
![Command String] [Command Parameters]
So you will want a class that represents that:
public class ChatCommand
{
private string _command;
public string Command { get { return _command; } }
private string _parameters;
public string Parameters { get { return _parameters; } }
public ChatCommand(string command, string parameters) {
_command = command;
_parameters = parameters;
}
}
From there, you will need to adjust the method you posted to look like this. Notice that we are now telling split to stop splitting when it has 2 strings to return (basically splitting on the first space only).
public void skype_MessageStatus(ChatMessage msg, TChatMessageStatus status)
{
if (msg.Body.IndexOf(trigger) == 0 && TChatMessageStatus.cmsReceived == status)
{
string command = msg.Body.Remove(0, trigger.Length).ToLower();
var splitted = command.Split(new [] { ' ' }, 2);
var command1 = new ChatCommand(splitted[0], splitted[1])
msg.Chat.SendMessage(nick + ProcessCommand(command1));
}
}
The reason we are leaving the parameters in one string instead of splitting them out is because this gives you the flexibility to format parameters for different commands differently. For example, maybe you want to have a command to send a picture and the second parameters is a URL, or it could be JSON data or binary file data.
Now your ProcessCommand function can look like this:
public void ProcessCommand(ChatCommand command) {
switch(command.Command) {
case "command":
//I am code specific to that command and I should know what is contained in the Parameters property of the command
Console.WriteLine("Name is " + command.Parameters);
break;
}
}
Then you're done! You can add more commands with any parameters you would like.

Categories