First of all - I know this question has been asked. I hardly know C# still learning, a lot of this code is from a tutorial, so I was hoping if I could have a more of direct answer to my actual code. Im making a twitch bot.
private void ViewListUpdate()
{
ViewerBox.Items.Clear();
Chatters AllChatters = ChatClient.GetChatters("name");
chatBox.Text += "Checking the viewer list...";
foreach (string admin in AllChatters.Admins)
{
ViewerBox.Items.Add(admin + Environment.NewLine);
}
foreach (string staff in AllChatters.Staff)
{
ViewerBox.Items.Add(staff + Environment.NewLine);
}
foreach (string globalmod in AllChatters.GlobalMods)
{
ViewerBox.Items.Add(globalmod + Environment.NewLine);
}
foreach (string moderator in AllChatters.Moderators)
{
ViewerBox.Items.Add(moderator + Environment.NewLine);
}
foreach (string viewers in AllChatters.Viewers)
{
ViewerBox.Items.Add(viewers + Environment.NewLine);
}
}
The line that is getting the error (System.IndexOutOfRangeException: 'Index was outside the bounds of the array.') is the following:
Chatters AllChatters = ChatClient.GetChatters("name");
Any help would be great, thanks.
Compiled DLL
I have generated a compiled DLL for you here which you can download and add to your project. You can find this here: https://dropfile.to/9hzvwVX (updated)
Now you can fetch users for a channel like so:
var dataClient = new TwitchTmiClient();
var chatters = dataClient.GetChannelViewers("someTwitchChannelName");
Chatters will now contain a list of users in the active channel, separated by ranks (admin, mod, viewer etc.)
Explanation
Because this question is relevant to my personal interests I decided to add the feature you're looking for to the library I posted in the comments: https://github.com/michidk/TwitchCSharp
Of course, downloading files is always kind of sketchy. So what I did was add a new Twitch client implementation, because chatter data is not stored on the Twitch Kraken API but on the old, https://tmi.twitch.tv API.
namespace TwitchCSharp.Clients
{
public class TwitchTmiClient : ITwitchClient
{
public readonly RestClient restClient;
public TwitchTmiClient(string url = TwitchHelper.TwitchTmiUrl)
{
restClient = new RestClient(url);
restClient.AddHandler("application/json", new DynamicJsonDeserializer());
restClient.AddHandler("text/html", new DynamicJsonDeserializer());
restClient.AddDefaultHeader("Accept", TwitchHelper.twitchAcceptHeader);
}
public ViewerList GetChannelViewers(string channel)
{
var request = new RestRequest("group/user/{channel}/chatters");
request.AddUrlSegment("channel", channel.ToLower());
return restClient.Execute<ViewerList>(request).Data;
}
public RestRequest GetRequest(string url, Method method)
{
return new RestRequest(url, method);
}
}
}
This new Twitch client uses two models to deserialize json into:
namespace TwitchCSharp.Models
{
public class ViewerList
{
[JsonProperty("_links")]
public Dictionary<string, string> Links;
[JsonProperty("chatter_count")]
public int ChatterCount;
[JsonProperty("chatters")]
public Chatter Chatters;
}
}
...
namespace TwitchCSharp.Models
{
public class Chatter
{
[JsonProperty("moderators")] public string[] Moderators;
[JsonProperty("staff")] public string[] Staff;
[JsonProperty("admins")] public string[] Admins;
[JsonProperty("global_mods")] public string[] GlobalMods;
[JsonProperty("viewers")] public string[] Viewers;
}
}
A repository where you can see all the changes can be found here: https://github.com/nbokmans/TwitchCSharp/commit/ec38eecf1d0fbcb0b75c5de597a44582a61deb3d
You can git clone above repository and open it in Visual Studio to build it yourself, if you want to.
Related
Apologies as this may seem a dumb question but I am missing something and not sure what to do.
As part of an application I'm making I am using CSVHelper for creating a file which contains details of any errors that may arise when using the application, such as a failure to connect to the database, values missing etc. I had been following this tutorial here: https://www.youtube.com/watch?v=fRaSeLYYrcQ and edited it more to suit my needs. I have an error logging file like so:
public class ErrorLogging
{
public string ErrorType { get; set; }
public string Relation { get; set; }
public string Message { get; set; }
public static List<ErrorLogging> GetErrors(string type, string relating, string message)
{
return new List<ErrorLogging>
{
new ErrorLogging
{
ErrorType = type,
Relation = relating,
Message = message
}
};
}
}
So here I have made it so the column headers in the csv file will be ErrorType, Relating and Message
I also have an export log file:
class ExportLog
{
public static void Log<T>(string path, string file, List<T>report)
{
var csvPath = Path.Combine(path + "\\", file);
using (var streamWriter = new StreamWriter(csvPath))
{
using (var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture))
{
csvWriter.WriteRecords(report);
}
}
}
}
In my main file program.cs I then use these by doing something like:
if (validateRoleTitles is not false)
{
log.Information("Roles found");
}
else
{
log.Warning("Roles could not be found");
ErrorLogging.GetErrors("Warning", "Database", "Not all roles found in the database");
}
And I now need to create the csv file with the list of errors by doing something like:
ExportLog.Log(config.ExportFolder, exportFile, "error logging list here");
However I'm not sure how to actually display these lists that I am creating if that makes sense?
As I can not do:
ExportLog.Log(config.ExportFolder, exportFile, ErrorLogging.GetErrors());
since it will be looking for parameters to be passed in. I know I am missing something very obvious here but can't figure out what, any help would be appreciated
So I ended up solving my issue, I had been going about it the wrong way, I did not need to have:
public static List<ErrorLogging> GetErrors(string type, string relating, string message)
{
return new List<ErrorLogging>
{
new ErrorLogging
{
ErrorType = type,
Relation = relating,
Message = message
}
};
}
in ErrorLogging.cs, it simply just needed to be:
public class ErrorLogging
{
public string ErrorType { get; set; }
public string Relation { get; set; }
public string Message { get; set; }
}
Then in program.cs I can declare a new list like so:
List<ErrorLogging> ErrorList = new List<ErrorLogging>();
and then when there is a point I would need to add to the list I can simply do something along the lines of:
ErrorList.Add(new ErrorLogging()
{
ErrorType = "Warning",
Relation = "Database",
Message = "Not all properties found in the database"
});
And then when it comes to creating the csv with the list of errors, I can check whether the list is empty or not, if not empty then create:
if (ErrorList.Any())
{
ExportLog.Log(config.LogFolder, errorLog, ErrorList);
}
I have been working on a save game solution for my project and have hit a wall. After several days of research, trial/ error etc. I decided to give the stack a shot. I am attempting to convert back from a Json text file into an object, then add it to a dictionary. It keeps giving my Invalid cast exceptions, regardless as to how I iterate through the Jdata object. Keeping in mind I am using LitJson 3rd party. Here is the code thus far. The error occurs at the foreach statement.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using LitJson;
using System.IO;
public static class SaveGame {
public static string savePath = Application.persistentDataPath +
"/Saves/";
public static int numberOfSaves = 0;
public static string saveFileName = PlayerCrew.playerShipName + ".json";
public static void SavePlayerData ()
{
string playerSavePath = savePath + saveFileName;
string jsonHolder;
jsonHolder = JsonMapper.ToJson(PlayerCrew.playerCrewManifest);
if (!File.Exists(playerSavePath))
{
FileStream fs = new FileStream(playerSavePath,
FileMode.OpenOrCreate);
fs.Close();
File.WriteAllText(playerSavePath, jsonHolder);
}
else
{
File.WriteAllText(playerSavePath, jsonHolder);
}
}
public static void LoadCrewManifest()
{
string playerSavePath = savePath + saveFileName;
string jsonHolder;
jsonHolder = File.ReadAllText(playerSavePath);
JsonData jdata = JsonMapper.ToObject(jsonHolder);
PlayerCrew.playerCrewManifest.Clear();
foreach (KeyValuePair<string,CrewMember> item in jdata)
{
PlayerCrew.playerCrewManifest.Add(item.Key, item.Value);
Debug.Log(item.Key);
}
}
}
I recommend you to use NetJson. You can Deserialize using a generic type, including Dictionary.
The values in jdata might come as KeyValuePair<string, string>
the simplest way would be a simple constructor for your class CrewMember e.g. something like
[Serializable]
public class CrewMember
{
public string Name;
public CrewMember(string name)
{
Name = name;
}
}
Than you don't want the item.key since it will be the variable name (in this case Name) instead you want the item.Value.
Your json code could look like
JsonData jdata = JsonMapper.ToObject(jsonHolder);
PlayerCrew.playerCrewManifest.Clear();
foreach (KeyValuePair<string,string> item in jdata)
{
PlayerCrew.playerCrewManifest.Add(item.Value, new CrewMember(item.Value));
Debug.Log(item.Value);
}
I have some Config files as part of my solution on Windows Mobile. I am porting the code to MonoForAndroid and MonoTouch, so I want to keep my code unchanged as much as possible.
When loading these xml files, on Windows Mobile works fine, in my last prototype it also worked on iOS, but the code does not work on MonForAndroid
I have these files
/solution folder
/My Documents/
/Business
App.Config
Settings.Config
I have these files build action set to Content and I can see that they are being copied to the /bin/Debug/ but When I try to read these files, I get the following exception:
System.IO.DirectoryNotFoundException
I see that there is a similar question in here, but they advised to use AndroidResources, which I do not want to do, there are many placed where these files are needed, so I do not want to change it in many places.
AndrodiResources, is out of the question, and if possible I would like to avoid using EmbededResources
ah and the way I am reading it, very straightforward xmDoc.Load(filePath) I also tried File.ReadAllText() I made sure that the filePath is correct, and I got the path generated using Path.Combine() to avoid any issues with the filePath/slashes
Here is how I construct my file path
var filePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace(FileURIPrefix, ""), "My Documents", "Business");
filePath = Path.Combine(filePath, "App.Config");
And I can see in the debugger that the filePath is correct
Thanks for the help in advance
After searching all around, I could not get the MonoDroid to load (or include) my files when their build action is set to Content.
I had to create an entity called FileHelper which is implemented differently on Android, I then use that FileHelper.ReadAllText(string filename);
I will put my implementation here, hoping that it would benefit somebody else.
Windows Mobile and iOS
public class FileHelper
{
public static string ReadAllText(string filePath)
{
var path = filePath.GetFullPath();
if (!File.Exists(path))
{
Logging.LogHandler.LogError("File " + path + " does not exists");
return string.Empty;
}
using (var reader = new StreamReader(filePath))
{
return reader.ReadToEnd();
}
}
}
Android version
public class FileHelper : BaseFileHelper
{
public static string ReadAllText(string filePath)
{
var entryAssemblyPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace("file:", ""), "MyExecutableAssemblyName.dll");
// This is because Assembly.GetEntryAssembly() returns null on Android... Booohhh
var assembly = Assembly.LoadFrom(entryAssemblyPath);
using (var stream = assembly.GetManifestResourceStream(filePath.GetFullPath()))
{
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
}
I had a shared code for Constants and an extention method for paths as below
Constants.cs
public static Class Constants
{
private static string _RootPath;
private static string _iOSRootPath;
private static string _AndroidResourcePath;
public static string RootPath
{
get
{
if (string.IsNullOrEmpty(_RootPath))
{
_RootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace(FileURIPrefix, "") + "\\My Documents\\Business";
}
return _RootPath;
}
}
public static string iOSRootPath
{
get
{
if (!string.IsNullOrEmpty(_iOSRootPath))
{
_iOSRootPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace(FileURIPrefix, "").Replace("file:", ""), Path.Combine("My_Documents", "Business"));
}
return _iOSRootPath;
}
}
public static string AndroidResourcePath
{
get
{
if (string.IsNullOrEmpty(_AndroidResourcePath))
{
_AndroidResourcePath = "Leopard.Delivery.My_Documents.Business.";
}
return _AndroidResourcePath;
}
}
}
PathExtentions.cs
public static class PathExtensions
{
public static string GetFullPath(this string filePath)
{
if (Platform.IsAndroid) // platform is a class that I have to tell me which platfrom I am at :)
{
return Constants.AndroidResourcePath + filePath;
}
if (Platform.IsIOS)
{
return Path.Combine(Constants.iOSRootPath, filePath);
}
return Path.Combine(Constants.RootPath, filePath);
}
}
After setting this up, I am using my FileHelper just as easy as below
string configuratinContents = FileHelper.ReadAllText(configruationPath);
To whoever using this code, remember to set the build action to EmbededResources on Android, and to Content on iOS and Windows Mobile.
I have the following code:
dynamic users = BLClass.MYBLMethod();
List<string> usernames = BLClass.MYBLSecondMethod();
foreach (string username in usernames)
{
users[username].test();
}
I am fetching users(dynamic type) from a BL method. Next from other method I am fetching list of usernames. After that I am applying foreach on list of usernames. In this loop I am calling a method "test". This "test" method will be used in my JavaScript code. Now my question is how can I convert the above code to some extension method like this:
BLClass.SomeExtensionMethod(methodname)
i.e my SomeExtensionMethod will cover the following logic in it:
dynamic users = BLClass.MYBLMethod();
List<string> usernames = BLClass.MYBLSecondMethod();
foreach (string username in usernames)
{
users[username].methodname();
}
Can you help me making this extension?
EDIT:
My goal is to have BLClass.SomeExtensionMethod(methodname) . I will pass the method name in it and ther inner code(inside the extension) should call this code :
users[username].methodname();
I don't know exactly what your types look like, but you can call a method by its name using GetMethod().
public static class Extensions
{
public static void SomeExtensionMethod(this BLClass blclass, string methodname)
{
dynamic users = blclass.MYBLMethod();
List<string> usernames = blclass.MYBLSecondMethod();
foreach (string username in usernames)
{
var user = users[username];
user.GetType().GetMethod(methodname).Invoke(user, null);
}
}
}
Not sure why you need to do this...
public class 1 {
public void method(){
//do stuff
}
}
public class 2 {
public void method2(){
dynamic users = BLClass.MYBLMethod();
List<string> usernames = BLClass.MYBLSecondMethod();
foreach (string username in usernames)
{
1.method(users[username]);
}
}
}
Would this be simpler?
I am working with AIMLbot.dll in c#. I saw two functions saveToBinaryFile and loadFromBinaryFile. I think these functions are to store current contents in bot's brain to a file. But it doesn't seems to be working! Means, If I say to remember my name and save the content to GraphMaster.dat file. Next time I load the content of the same file and when I ask my name its giving the wrong answer. My class is as follows.
class AIBot
{
private Bot myBot;
private User myUser;
public AIBot()
{
myBot = new Bot();
myUser = new User("UnknownUser", myBot);
}
public void Initialize()
{
myBot.loadSettings();
myBot.isAcceptingUserInput = false;
myBot.loadAIMLFromFiles();
myBot.isAcceptingUserInput = true;
}
public void Load()
{
if (File.Exists(AppDomain.CurrentDomain.BaseDirectory + #"\Graphmaster.dat"))
myBot.loadFromBinaryFile(AppDomain.CurrentDomain.BaseDirectory + #"\Graphmaster.dat");
}
public string GetResponse(string input)
{
Request r = new Request(input, myUser, myBot);
Result res = myBot.Chat(r);
return (res.Output);
}
public void Save()
{
myBot.saveToBinaryFile(AppDomain.CurrentDomain.BaseDirectory + #"\Graphmaster.dat");
}
}
Can anybody help to point out the problem?
I got a solution for the problem. Hope it will help others.
Save user session like
this.myUser.Predicates.DictionaryAsXML.Save(saveFileDialogDump.FileName);
Load the stored session next time.
this.myUser.Predicates.loadSettings(openFileDialogDump.FileName);