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.
Related
is there anyone check my code?
it shows the parameter count dose not match.
void WriteFaceLog(string userID, string faceId, string size, string valid, string template)
{
if (lstvFace.InvokeRequired)
{
Action<string, string, string, string, string> action = WriteFaceLog;
this.Invoke(action, faceId, size, valid, template);
}
else
{
ListViewItem item = new ListViewItem(userID);
item.SubItems.AddRange(new[] { faceId, size, valid, template });
lstvFace.Items.Add(item);
}
}
The typical auto-invoke pattern (designed to simplify running method from any thread) will looks like this in your case:
void WriteFaceLog(string userID, string faceId, string size, string valid, string template)
{
if (this.InvokeRequired) // you can use lstvFace instead of this, it doesn't matter as both are in same thread, you can also omit this
this.Invoke((MethodInvoker)delegate { WriteFaceLog(userID, faceId, size, valid, template); });
else
{
}
}
In given case you are unlikely to miss any parameter (just re-pass them).
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.
I am writing a language of my own called SPL. It has an Input command which reads input from the ISplRuntime.Input property (it is a TextReader). All other commands run on this interface because this way I can write different apps with just one library!
I then wrote another console app to test my language. This is my implementation of ISplRuntime. Focus on the Input and constructor:
public class MyRuntime : ISplRuntime {
protected TextReader reader;
protected bool stopped;
public object Current {
get;
set;
}
public virtual TextReader Input {
get {
return reader;
}
}
public object[] Memory {
get;
protected set;
}
public TextWriter Output {
get {
return Console.Out;
}
}
public bool Stopped {
get {
return stopped;
}
set {
stopped = value;
if (value) {
Console.WriteLine ();
Console.WriteLine ("Program has finished");
}
}
}
public void ShowErrorMessage (string error) {
Console.WriteLine (error);
}
public MyRuntime () {
string s = Console.ReadLine ();
reader = new StringReader (s);
stopped = false;
Memory = new object[20];
}
}
When the runtime is constructed, it asks for input. And use that input to create a StringReader and return it in the Input property. So every time the input will only be one lline.
Then I write a program in SPL that outputs the input. And that is where the problem is! When I input 1 1 1 1 it prints 1 1 1 and threw a FormatException. This is how I read number input:
private bool ReadFromInput (ISplRuntime runtime, out int i) {
char stuffRead = (char)runtime.Input.Peek ();
if (stuffRead == ' ') {
i = 0;
runtime.Input.Read ();
return true;
}
if (char.IsNumber (stuffRead)) {
string numberString = "";
while (char.IsNumber (stuffRead)) {
stuffRead = (char)runtime.Input.Read ();
numberString += stuffRead;
}
i = Convert.ToInt32 (numberString); //This is where the exception occured! (Obviously, because there is no other methods that would throw it)
return true;
} else {
i = 0;
return false;
}
}
The parameter runtime is just the runtime you have just seen. It returns true if it successfully reads a number. And that number is the output parameter i.
After using the "Watch" window in Visual Studio, I found out that number string is "1\uffff" when the exception is thrown. That's why it throws it! I know (think) that '\uffff' is the end of line character. But why would it appear in my input? I know (think) that pressing Ctrl + Z makes a end of line, but I did not! Then I checked runtime.Input in the watch window. This is the result:
I see that there is a field called _s and I think that is the string that I told it to read from. See? _s doesn't even contain '\uffff', how come it reads it?
P.S. I already know the solution. I just need to change the while loop a little and it works. But I want to know why does it reads an end of line.
There is no mistery here - \uffff is produced by your code. All you need is to read the documentation and understand what the methods that you call return.
TextReader.Peek Method
Return Value
Type: System.Int32
An integer representing the next character to be read, or -1 if no more characters are available or the reader does not support seeking.
TextReader.Read Method
Return Value
Type: System.Int32
The next character from the text reader, or -1 if no more characters are available.
Hope you see the relation between -1 (0xffffffff) and \uffff.
So my question is over basic encapsulation. I know I am setting up my getters and setters right (I actually have a question later about this) but I have multiple classes. I have another class and I understand that I am making pieces of my code view-able to my outside class by making certain things public. So I think I set up my first code file right. (Some background, I have a class that is connecting to a database and then another that is encapsulating all the data. The first code section posted is the encapsulating part, I then post my three methods I was messing up on.)
I feel okay with the getting and setting, I feel a little unsure of my constructor. I feel like I put my variables in the parameter list so that I put values in them from my outside class? Right? or should I be putting public forms of my private variables in my other code file and then passing those into my constructor in that same file?
/ this my first code file
using System;
public class clsEncapsulate
{
private int mID;
private string mName;
private string mClassification;
private DateTime mConvocationDate;
private string mLocation;
public int ID
{
get
{
return mID;
}
set
{
mID = value;
}
}
public string Name
{
get
{
return mName;
}
set
{
mName = value;
}
}
public string Classification
{
get
{
return mName;
}
set
{
mName = value;
}
}
private DateTime ConvocationDate
{
get
{
return mConvocationDate;
}
set
{
mConvocationDate = value;
}
}
private string Location
{
get
{
return mLocation;
}
set
{
mLocation = value;
}
}
public clsEncapsulate(int id, string name, string classification, DateTime convocationDate, string location)
{
bool running = false;
while(running == false)
{
ID = mID;
Name = mName;
Classification = mClassification;
ConvocationDate = mConvocationDate;
Location = mLocation;
running = true;
}
}
}
In my second code file I am just going to put the methods that I am having trouble with.
private void refreshbox()
{
string formattedConvocation;
string formattedDateTime;
string formattedConvocationName;
lstConvocations.Items.Clear();
foreach (clsEncapsulate currentConvocation in mConvocationAL)
{
formattedConvocationName = currentConvocation.Name;
formattedConvocationName = truncateString(formattedConvocationName, 30);
formattedConvocation = formattedConvocationName.PadRight(33);
formattedConvocation += currentConvocation.Classification.PadRight(17);
formattedDateTime = currentConvocation.ConvocationDate.ToShortDateString().PadRight(10)
+ currentConvocation.ConvocationDate.ToShortTimeString().PadLeft(8);
formattedConvocation += formattedDateTime;
lstConvocations.Items.Add(formattedConvocation);
}
}
Alright, so in order for my second code file to manipulate the variables in the first code file, I need to expose them to this method. I didn't know if I should be putting my public variables in the constructor, or if I should be declaring them somewhere in my first code file. I was very unsure of how to expose these variables to this method. I've fiddle around with it but my book doesn't address this situation exactly and I was having trouble figuring it out.
If someone does answer this question please break down why you're going to put what you're going to put! I want to understand why, say, I put my public variables in one place, and not another. Or why I declare an object of my encapsulate class in one place and not another. I was trying to declare an encapsulate object in my method so it would give this method access to the variables, but it wasn't working! Please tell me what I was doing wrong or if you want me to post more of my code.
Below are the two other methods I was messing up on.
/ second method from my second code file I was messing up on:
private void displayProperties(int index)
{
if (index == -1)
{
return;
}
clsEncapsulate selectedValue = (clsEncapsulate)mConvocationAL[index];
txtConvocationName.Text = selectedValue.Name;
txtConvocationClassification.Text = selectedValue.Classification;
txtConvocationDate.Text = selectedValue.ConvocationDate.ToShortDateString();
txtConvocationTime.Text = selectedValue.ConvocationDate.ToShortTimeString();
txtConvocationLocation.Text = selectedValue.Location;
txtID.Text = selectedValue.ID.ToString();
}
/ last method I was messing up on:
private void readConvocations(string filterConstraint, string sortField, string sortOrder)
{
OleDbConnection connection = null;
OleDbDataReader reader = null;
try
{
connection = new OleDbConnection();
connection.ConnectionString = mConnectionString;
connection.Open();
string statement = "SELECT ID, Name, Classification, Location, Date FROM Convocations ";
if(filterConstraint != "")
{
statement += "WHERE Name LIKE " + toSQL(filterConstraint, true) + " ";
}
string statement2 = statement;
statement = string.Concat(new string[]
{
statement2, "ORDER BY ", sortField, " ", sortOrder
});
OleDbCommand oleDbCommand = new OleDbCommand(statement, connection);
reader = oleDbCommand.ExecuteReader();
mConvocationAL.Clear();
while(reader.Read())
{
clsEncapsulteconvocation = new clsEncapsulate();
convocation.ID = (int)reader["ID"];
convocation.Name = (string)reader["Name"];
convocation.Classification = (string)reader["Classification"];
convocation.Location = (string)reader["Location"];
convocation.ConvocationDate = (DateTime)reader["Date"];
mConvocationAL.Add(convocation);
}
}
finally
{
if (reader != null)
{
reader.Close();
}
if (connection != null)
{
connection.Close();
}
}
}
Tell me if you need me to elaborate more to help you understand my situation. I am new at learning vocabulary and want to understand this! Thank you for helping. :)
The code you provided is one public object and a bunch of private methods so its difficult to get the overall picture of how your code it working together but there are a few principles you can apply to make your code better structured, now and in the future.
Have a read about SOLID (http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)). The S and the D would apply well to your example.
Also you mentioned construtors and private properties. Try looking into Imutable types. That means once the object is created you cannot change it. For your clsEncapsulate class that would mean making your fields read only and remove the public setters.
Good luck.
I'm using the Windows Event Log to record some events. Events within the Windows Event Log can be assigned a handful of properties. One of which, is an EventID.
Now I want to use the EventId to try and group related errors. I could just pick a number for each call to the logging method I do, but that seems a little tedious.
I want the system to do this automatically. It would choose an eventId that is "unique" to the position in the code where the logging event occurred. Now, there's only 65536 unique event IDs, so there are likely to be collisions but they should be rare enough to make the EventId a useful way to group errors.
One strategy would be to take the hashcode of the stacktrace but that would mean that the first and second calls in the following code would have generate the same event ID.
public void TestLog()
{
LogSomething("Moo");
// Do some stuff and then a 100 lines later..
LogSomething("Moo");
}
I thought of walking up the call stack using the StackFrame class which has a GetFileLineNumber method. The problem with this strategy is that it will only work when built with debug symbols on. I need it to work in production code too.
Does anyone have any ideas?
Here is some code you can use to generate an EventID with the properties I describe in my question:
public static int GenerateEventId()
{
StackTrace trace = new StackTrace();
StringBuilder builder = new StringBuilder();
builder.Append(Environment.StackTrace);
foreach (StackFrame frame in trace.GetFrames())
{
builder.Append(frame.GetILOffset());
builder.Append(",");
}
return builder.ToString().GetHashCode() & 0xFFFF;
}
The frame.GetILOffset() method call gives the position within that particular frame at the time of execution.
I concatenate these offsets with the entire stacktrace to give a unique string for the current position within the program.
Finally, since there are only 65536 unique event IDs I logical AND the hashcode against 0xFFFF to extract least significant 16-bits. This value then becomes the EventId.
The IL offset number is available without debug symbols. Combined with the stack information and hashed, I think that would do the trick.
Here's an article that, in part, covers retrieving the IL offset (for the purpose of logging it for an offline match to PDB files--different problem but I think it'll show you what you need):
http://timstall.dotnetdevelopersjournal.com/getting_file_and_line_numbers_without_deploying_the_pdb_file.htm
Create a hash using the ILOffset of the last but one stack frame instead of the line number (i.e. the stack frame of your TestLog method above).
*Important: This post focuses at solving the root cause of what it appears your problem is instead of providing a solution you specifically asked for. I realize this post is old, but felt it important to contribute. *
My team had a similar issue, and we changed the way we managed our logging which has reduced production support and bug patching times significantly. Pragmatically this works in most enterprise apps my team works on:
Prefix log messages with the "class name"."function name".
For true errors, output the captured Exception to the event logger.
Focus on having clear messages as part of the peer code review as opposed to event id's.
Use a unique event id for each function, just go top to bottom and key them.
when it becomes impractical to code each function a different event ID, each class should just just have a unique one (collisions be damned).
Utilize Event categories to reduce event id reliance when filtering the log
Of course it matters how big your apps are and how sensitive the data is. Most of ours are around 10k to 500k lines of code with minimally sensitive information. It may feel oversimplified, but from a KISS standpoint it pragmatically works.
That being said, using an abstract Event Log class to simplify the process makes it easy to utilize, although cleanup my be unpleasant. For Example:
MyClass.cs (using the wrapper)
class MyClass
{
// hardcoded, but should be from configuration vars
private string AppName = "MyApp";
private string AppVersion = "1.0.0.0";
private string ClassName = "MyClass";
private string LogName = "MyApp Log";
EventLogAdapter oEventLogAdapter;
EventLogEntryType oEventLogEntryType;
public MyClass(){
this.oEventLogAdapter = new EventLogAdapter(
this.AppName
, this.LogName
, this.AppName
, this.AppVersion
, this.ClassName
);
}
private bool MyFunction() {
bool result = false;
this.oEventLogAdapter.SetMethodInformation("MyFunction", 100);
try {
// do stuff
this.oEventLogAdapter.WriteEntry("Something important found out...", EventLogEntryType.Information);
} catch (Exception oException) {
this.oEventLogAdapter.WriteEntry("Error: " + oException.ToString(), EventLogEntryType.Error);
}
return result;
}
}
EventLogAdapter.cs
class EventLogAdapter
{
//vars
private string _EventProgram = "";
private string _EventSource = "";
private string _ProgramName = "";
private string _ProgramVersion = "";
private string _EventClass = "";
private string _EventMethod = "";
private int _EventCode = 1;
private bool _Initialized = false;
private System.Diagnostics.EventLog oEventLog = new EventLog();
// methods
public EventLogAdapter() { }
public EventLogAdapter(
string EventProgram
, string EventSource
, string ProgramName
, string ProgramVersion
, string EventClass
) {
this.SetEventProgram(EventProgram);
this.SetEventSource(EventSource);
this.SetProgramName(ProgramName);
this.SetProgramVersion(ProgramVersion);
this.SetEventClass(EventClass);
this.InitializeEventLog();
}
public void InitializeEventLog() {
try {
if(
!String.IsNullOrEmpty(this._EventSource)
&& !String.IsNullOrEmpty(this._EventProgram)
){
if (!System.Diagnostics.EventLog.SourceExists(this._EventSource)) {
System.Diagnostics.EventLog.CreateEventSource(
this._EventSource
, this._EventProgram
);
}
this.oEventLog.Source = this._EventSource;
this.oEventLog.Log = this._EventProgram;
this._Initialized = true;
}
} catch { }
}
public void WriteEntry(string Message, System.Diagnostics.EventLogEntryType EventEntryType) {
try {
string _message =
"[" + this._ProgramName + " " + this._ProgramVersion + "]"
+ "." + this._EventClass + "." + this._EventMethod + "():\n"
+ Message;
this.oEventLog.WriteEntry(
Message
, EventEntryType
, this._EventCode
);
} catch { }
}
public void SetMethodInformation(
string EventMethod
,int EventCode
) {
this.SetEventMethod(EventMethod);
this.SetEventCode(EventCode);
}
public string GetEventProgram() { return this._EventProgram; }
public string GetEventSource() { return this._EventSource; }
public string GetProgramName() { return this._ProgramName; }
public string GetProgramVersion() { return this._ProgramVersion; }
public string GetEventClass() { return this._EventClass; }
public string GetEventMethod() { return this._EventMethod; }
public int GetEventCode() { return this._EventCode; }
public void SetEventProgram(string EventProgram) { this._EventProgram = EventProgram; }
public void SetEventSource(string EventSource) { this._EventSource = EventSource; }
public void SetProgramName(string ProgramName) { this._ProgramName = ProgramName; }
public void SetProgramVersion(string ProgramVersion) { this._ProgramVersion = ProgramVersion; }
public void SetEventClass(string EventClass) { this._EventClass = EventClass; }
public void SetEventMethod(string EventMethod) { this._EventMethod = EventMethod; }
public void SetEventCode(int EventCode) { this._EventCode = EventCode; }
}
Thanks for the idea of hashing the call stack, I was going to ask that very same question of how to pick an eventId.
I recommend putting a static variable in LogSomething that increments each time it is called.
Now I want to use the EventId to try
and group related errors.
You have filters in event viewer so why (Go to find ? You have 65536 unique event IDs too.
Or rather use log4net or something ??
just my ideas....