Guest User

Untitled

a guest
Aug 17th, 2016
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 143.00 KB | None | 0 0
  1. using System;
  2. using System.Threading.Tasks;
  3. using Newtonsoft.Json;
  4. using System.Net;
  5. using System.IO;
  6. using WebSocketSharp;
  7. using System.Threading;
  8. using System.Collections.Generic;
  9. using Newtonsoft.Json.Linq;
  10. using DiscordSharp.Events;
  11. using System.Text.RegularExpressions;
  12. using System.Drawing;
  13. using DiscordSharp.Objects;
  14.  
  15. using ID = System.String;
  16. using DiscordSharp.Sockets;
  17.  
  18. namespace DiscordSharp
  19. {
  20. /// <summary>
  21. /// Properties that Discord uses upon connection to the websocket. Mostly used for analytics internally.
  22. /// </summary>
  23. public class DiscordProperties
  24. {
  25. /// <summary>
  26. /// The OS you're on
  27. /// </summary>
  28. [JsonProperty("$os")]
  29. public string OS { get; set; }
  30.  
  31. /// <summary>
  32. /// The "browser" you're using.
  33. /// </summary>
  34. [JsonProperty("$browser")]
  35. public string Browser { get; set; }
  36.  
  37. /// <summary>
  38. /// Whatever device you want to be on. (Default: DiscordSharp Bot)
  39. /// </summary>
  40. [JsonProperty("$device")]
  41. public string Device
  42. { get; set; } = "DiscordSharp Bot";
  43.  
  44. /// <summary>
  45. ///
  46. /// </summary>
  47. [JsonProperty("$referrer")]
  48. public string referrer { get; set; }
  49. /// <summary>
  50. ///
  51. /// </summary>
  52. [JsonProperty("$referring_domain")]
  53. public string referring_domain { get; set; }
  54.  
  55. /// <summary>
  56. /// Default constructor setting the OS property to Environment.OSVersion.ToString();
  57. /// </summary>
  58. public DiscordProperties()
  59. {
  60. OS = Environment.OSVersion.ToString();
  61. }
  62.  
  63. /// <summary>
  64. /// Serializes this object as json.
  65. /// </summary>
  66. /// <returns>Json string of this object serialized</returns>
  67. public string AsJson()
  68. {
  69. return JsonConvert.SerializeObject(this);
  70. }
  71. }
  72.  
  73. /// <summary>
  74. /// The type of message that the Discord message is.
  75. /// </summary>
  76. public enum DiscordMessageType
  77. {
  78. /// <summary>
  79. /// Private/DM
  80. /// </summary>
  81. PRIVATE,
  82. /// <summary>
  83. /// Public/in a channel.
  84. /// </summary>
  85. CHANNEL
  86. }
  87.  
  88. /// <summary>
  89. /// Where the magic happens.
  90. /// </summary>
  91. public class DiscordClient
  92. {
  93. /// <summary>
  94. /// The token associated with authenticating your bot and ensuring they can send messages.
  95. /// </summary>
  96. public static string token { get; internal set; } = null;
  97. /// <summary>
  98. /// If this is true, the account this client is running on is a special bot account.
  99. /// </summary>
  100. public static bool IsBotAccount { get; internal set; } = false;
  101.  
  102. /// <summary>
  103. /// The URL that the client websocket is currently connected to.
  104. /// </summary>
  105. public string CurrentGatewayURL { get; internal set; }
  106.  
  107. /// <summary>
  108. /// Any private information assosciated with the account (regular clients only)
  109. /// </summary>
  110. public DiscordUserInformation ClientPrivateInformation { get; set; }
  111.  
  112. /// <summary>
  113. /// Custom properties containing parameters such as
  114. /// * OS
  115. /// * Referrer
  116. /// * Browser
  117. /// Used by Discord internally for connection.
  118. /// </summary>
  119. public DiscordProperties DiscordProperties { get; set; } = new DiscordProperties();
  120.  
  121. /// <summary>
  122. /// The current DiscordMember object assosciated with the account you're connected to.
  123. /// </summary>
  124. public DiscordMember Me { get; internal set; }
  125.  
  126. /// <summary>
  127. /// Returns the debug logger used to log various debug events.
  128. /// </summary>
  129. public Logger GetTextClientLogger => DebugLogger;
  130.  
  131. /// <summary>
  132. /// Returns the last debug logger for when the voice client was last connected.
  133. /// </summary>
  134. public Logger GetLastVoiceClientLogger;
  135.  
  136. /// <summary>
  137. /// If true, the logger will log everything.
  138. /// Everything.
  139. /// </summary>
  140. public bool EnableVerboseLogging { get; set; } = false;
  141.  
  142. /// <summary>
  143. /// The version of the gateway.
  144. /// </summary>
  145. public int DiscordGatewayVersion { get; set; } = 0;
  146.  
  147. [Obsolete]
  148. internal bool V4Testing { get; set; } = false;
  149.  
  150. /// <summary>
  151. /// V4 related things. Getting this means our session has been successfully initiated.
  152. /// </summary>
  153. private string SessionID;
  154.  
  155. /// <summary>
  156. /// The last sequence we received used for v4 heartbeat.
  157. /// </summary>
  158. private int Sequence = 0;
  159.  
  160. /// <summary>
  161. /// Whether or not to send Opcode 6 (resume) upon a socket being closed.
  162. /// </summary>
  163. public bool Autoconnect { get; set; } = true;
  164.  
  165.  
  166. private IDiscordWebSocket ws;
  167. private List<DiscordServer> ServersList { get; set; }
  168. private string CurrentGameName = "";
  169. private int? IdleSinceUnixTime = null;
  170. static string UserAgentString = $" (http://github.com/Luigifan/DiscordSharp, {typeof(DiscordClient).Assembly.GetName().Version.ToString()})";
  171. private DiscordVoiceClient VoiceClient;
  172. private Logger DebugLogger = new Logger();
  173. private CancellationTokenSource KeepAliveTaskTokenSource = new CancellationTokenSource();
  174. private CancellationToken KeepAliveTaskToken;
  175. private Task KeepAliveTask;
  176. private Thread VoiceThread; //yuck
  177. private static string StrippedEmail = "";
  178.  
  179. /// <summary>
  180. /// Testing.
  181. /// </summary>
  182. private List<DiscordMember> RemovedMembers = new List<DiscordMember>();
  183.  
  184. /// <summary>
  185. /// Whether or not to write the latest READY upon receiving it.
  186. /// If this is true, the client will write the contents of the READY message to 'READY_LATEST.txt'
  187. /// If your client is connected to a lot of servers, this file will be quite large.
  188. /// </summary>
  189. public bool WriteLatestReady { get; set; } = false;
  190.  
  191. /// <summary>
  192. /// Whether or not to request all users in a guild (including offlines) on startup.
  193. /// </summary>
  194. public bool RequestAllUsersOnStartup { get; set; } = false;
  195.  
  196. /// <summary>
  197. /// A log of messages kept in a KeyValuePair.
  198. /// The key is the id of the message, and the value is a DiscordMessage object. If you need raw json, this is contained inside of the DiscordMessage object now.
  199. /// </summary>
  200. private Dictionary<ID, DiscordMessage> MessageLog = new Dictionary<string, DiscordMessage>();
  201. //private List<KeyValuePair<string, DiscordMessage>> MessageLog = new List<KeyValuePair<string, DiscordMessage>>();
  202. private List<DiscordPrivateChannel> PrivateChannels = new List<DiscordPrivateChannel>();
  203.  
  204. #region Event declaration
  205. public event EventHandler<DiscordMessageEventArgs> MessageReceived;
  206. public event EventHandler<DiscordConnectEventArgs> Connected;
  207. public event EventHandler<EventArgs> SocketOpened;
  208. public event EventHandler<DiscordSocketClosedEventArgs> SocketClosed;
  209. public event EventHandler<DiscordChannelCreateEventArgs> ChannelCreated;
  210. public event EventHandler<DiscordPrivateChannelEventArgs> PrivateChannelCreated;
  211. public event EventHandler<DiscordPrivateMessageEventArgs> PrivateMessageReceived;
  212. public event EventHandler<DiscordKeepAliveSentEventArgs> KeepAliveSent;
  213. public event EventHandler<DiscordMessageEventArgs> MentionReceived;
  214. public event EventHandler<DiscordTypingStartEventArgs> UserTypingStart;
  215. public event EventHandler<DiscordMessageEditedEventArgs> MessageEdited;
  216. public event EventHandler<DiscordPresenceUpdateEventArgs> PresenceUpdated;
  217. public event EventHandler<DiscordURLUpdateEventArgs> URLMessageAutoUpdate;
  218. public event EventHandler<DiscordVoiceStateUpdateEventArgs> VoiceStateUpdate;
  219. public event EventHandler<UnknownMessageEventArgs> UnknownMessageTypeReceived;
  220. public event EventHandler<DiscordMessageDeletedEventArgs> MessageDeleted;
  221. public event EventHandler<DiscordUserUpdateEventArgs> UserUpdate;
  222. public event EventHandler<DiscordGuildMemberAddEventArgs> UserAddedToServer;
  223. public event EventHandler<DiscordGuildMemberRemovedEventArgs> UserRemovedFromServer;
  224. public event EventHandler<DiscordGuildCreateEventArgs> GuildCreated;
  225. /// <summary>
  226. /// Occurs when a guild becomes available after being unavailable.
  227. /// </summary>
  228. public event EventHandler<DiscordGuildCreateEventArgs> GuildAvailable;
  229. public event EventHandler<DiscordGuildDeleteEventArgs> GuildDeleted;
  230. public event EventHandler<DiscordChannelUpdateEventArgs> ChannelUpdated;
  231. public event EventHandler<LoggerMessageReceivedArgs> TextClientDebugMessageReceived;
  232. public event EventHandler<LoggerMessageReceivedArgs> VoiceClientDebugMessageReceived;
  233. public event EventHandler<DiscordChannelDeleteEventArgs> ChannelDeleted;
  234. public event EventHandler<DiscordServerUpdateEventArgs> GuildUpdated;
  235. public event EventHandler<DiscordGuildRoleDeleteEventArgs> RoleDeleted;
  236. public event EventHandler<DiscordGuildRoleUpdateEventArgs> RoleUpdated;
  237. public event EventHandler<DiscordGuildMemberUpdateEventArgs> GuildMemberUpdated;
  238. public event EventHandler<DiscordGuildBanEventArgs> GuildMemberBanned;
  239. public event EventHandler<DiscordPrivateChannelDeleteEventArgs> PrivateChannelDeleted;
  240. public event EventHandler<DiscordBanRemovedEventArgs> BanRemoved;
  241. public event EventHandler<DiscordPrivateMessageDeletedEventArgs> PrivateMessageDeleted;
  242.  
  243. #region Voice
  244. /// <summary>
  245. /// For use when connected to voice only.
  246. /// </summary>
  247. public event EventHandler<DiscordAudioPacketEventArgs> AudioPacketReceived;
  248. /// <summary>
  249. /// For use when connected to voice only.
  250. /// </summary>
  251. public event EventHandler<DiscordVoiceUserSpeakingEventArgs> UserSpeaking;
  252. /// <summary>
  253. /// For use when connected to voice only.
  254. /// </summary>
  255. public event EventHandler<DiscordLeftVoiceChannelEventArgs> UserLeftVoiceChannel;
  256. /// <summary>
  257. /// Occurs when the voice client is fully connected to voice.
  258. /// </summary>
  259. public event EventHandler<EventArgs> VoiceClientConnected;
  260. /// <summary>
  261. /// Occurs when the voice queue is emptied.
  262. /// </summary>
  263. public event EventHandler<EventArgs> VoiceQueueEmpty;
  264. #endregion
  265. #endregion
  266.  
  267. /// <summary>
  268. ///
  269. /// </summary>
  270. /// <param name="tokenOverride">If you have a token you wish to use, provide it here. Else, a login attempt will be made.</param>
  271. /// <param name="isBotAccount">Set this to true if your bot is going to be a bot account</param>
  272. public DiscordClient(string tokenOverride = null, bool isBotAccount = false, bool enableLogging = true)
  273. {
  274. if (isBotAccount && tokenOverride == null)
  275. throw new Exception("Token override cannot be null if using a bot account!");
  276. DebugLogger.EnableLogging = enableLogging;
  277.  
  278. token = tokenOverride;
  279. IsBotAccount = isBotAccount;
  280.  
  281. if (IsBotAccount)
  282. UserAgentString = "DiscordBot " + UserAgentString;
  283. else
  284. UserAgentString = "Custom Discord Client " + UserAgentString;
  285.  
  286. if (ClientPrivateInformation == null)
  287. ClientPrivateInformation = new DiscordUserInformation();
  288.  
  289. DebugLogger.LogMessageReceived += (sender, e) =>
  290. {
  291. if (e.message.Level == MessageLevel.Error)
  292. DisconnectFromVoice();
  293. if (TextClientDebugMessageReceived != null)
  294. TextClientDebugMessageReceived(this, e);
  295. };
  296. }
  297.  
  298. /// <summary>
  299. /// Current DiscordServers you're connected to.
  300. /// </summary>
  301. /// <returns>DiscordServer list of servers you're currently connected to.</returns>
  302. public List<DiscordServer> GetServersList() => ServersList;
  303.  
  304. /// <summary>
  305. /// Any messages logged since connection to the websocket.
  306. /// </summary>
  307. /// <returns>A KeyValuePair list of string-DiscordMessage. Where string is the message's ID</returns>
  308. public Dictionary<ID, DiscordMessage> GetMessageLog() => MessageLog;
  309.  
  310. /// <summary>
  311. /// Private channels assosciated with the account.
  312. /// </summary>
  313. /// <returns>a list of DiscordPrivateChannels.</returns>
  314. public List<DiscordPrivateChannel> GetPrivateChannels() => PrivateChannels;
  315.  
  316. /// <summary>
  317. ///
  318. /// </summary>
  319. /// <returns>True if connected to voice.</returns>
  320. public bool ConnectedToVoice() => VoiceClient != null ? VoiceClient.Connected : false;
  321.  
  322. //eh
  323. private void GetChannelsList(JObject m)
  324. {
  325. if (ServersList == null)
  326. ServersList = new List<DiscordServer>();
  327. foreach (var j in m["d"]["guilds"])
  328. {
  329. if (!j["unavailable"].IsNullOrEmpty() && j["unavailable"].ToObject<bool>() == true)
  330. continue; //unavailable server
  331. DiscordServer temp = new DiscordServer();
  332. temp.parentclient = this;
  333. temp.JoinedAt = j["joined_at"].ToObject<DateTime>();
  334. temp.ID = j["id"].ToString();
  335. temp.Name = j["name"].ToString();
  336. if (!j["icon"].IsNullOrEmpty())
  337. temp.icon = j["icon"].ToString();
  338. else
  339. temp.icon = null;
  340.  
  341. //temp.owner_id = j["owner_id"].ToString();
  342. List<DiscordChannel> tempSubs = new List<DiscordChannel>();
  343.  
  344. List<DiscordRole> tempRoles = new List<DiscordRole>();
  345. foreach (var u in j["roles"])
  346. {
  347. DiscordRole t = new DiscordRole
  348. {
  349. Color = new DiscordSharp.Color(u["color"].ToObject<int>().ToString("x")),
  350. Name = u["name"].ToString(),
  351. Permissions = new DiscordPermission(u["permissions"].ToObject<uint>()),
  352. Position = u["position"].ToObject<int>(),
  353. Managed = u["managed"].ToObject<bool>(),
  354. ID = u["id"].ToString(),
  355. Hoist = u["hoist"].ToObject<bool>()
  356. };
  357. tempRoles.Add(t);
  358. }
  359. temp.Roles = tempRoles;
  360. foreach (var u in j["channels"])
  361. {
  362. DiscordChannel tempSub = new DiscordChannel();
  363. tempSub.Client = this;
  364. tempSub.ID = u["id"].ToString();
  365. tempSub.Name = u["name"].ToString();
  366. tempSub.Type = u["type"].ToObject<ChannelType>();
  367. if (!u["topic"].IsNullOrEmpty())
  368. tempSub.Topic = u["topic"].ToString();
  369. if (tempSub.Type == ChannelType.Voice && !u["bitrate"].IsNullOrEmpty())
  370. tempSub.Bitrate = u["bitrate"].ToObject<int>();
  371. tempSub.Parent = temp;
  372. List<DiscordPermissionOverride> permissionoverrides = new List<DiscordPermissionOverride>();
  373. foreach (var o in u["permission_overwrites"])
  374. {
  375. DiscordPermissionOverride dpo = new DiscordPermissionOverride(o["allow"].ToObject<uint>(), o["deny"].ToObject<uint>());
  376. dpo.id = o["id"].ToString();
  377.  
  378. if (o["type"].ToString() == "member")
  379. dpo.type = DiscordPermissionOverride.OverrideType.member;
  380. else
  381. dpo.type = DiscordPermissionOverride.OverrideType.role;
  382.  
  383. permissionoverrides.Add(dpo);
  384. }
  385. tempSub.PermissionOverrides = permissionoverrides;
  386.  
  387. tempSubs.Add(tempSub);
  388. }
  389. temp.Channels = tempSubs;
  390. foreach (var mm in j["members"])
  391. {
  392. DiscordMember member = JsonConvert.DeserializeObject<DiscordMember>(mm["user"].ToString());
  393. member.parentclient = this;
  394. member.Roles = new List<DiscordRole>();
  395. JArray rawRoles = JArray.Parse(mm["roles"].ToString());
  396. if (rawRoles.Count > 0)
  397. {
  398. foreach (var role in rawRoles.Children())
  399. {
  400. member.Roles.Add(temp.Roles.Find(x => x.ID == role.Value<string>()));
  401. }
  402. }
  403. else
  404. {
  405. member.Roles.Add(temp.Roles.Find(x => x.Name == "@everyone"));
  406. }
  407. temp.AddMember(member);
  408. }
  409. if (!j["presences"].IsNullOrEmpty())
  410. {
  411. foreach (var presence in j["presences"])
  412. {
  413. DiscordMember member = temp.GetMemberByKey(presence["user"]["id"].ToString());
  414. if (member != null)
  415. {
  416. member.SetPresence(presence["status"].ToString());
  417. if (!presence["game"].IsNullOrEmpty())
  418. {
  419. member.CurrentGame = presence["game"]["name"].ToString();
  420. if (presence["d"]["game"]["type"].ToObject<int>() == 1)
  421. {
  422. member.Streaming = true;
  423. if (presence["d"]["game"]["url"].ToString() != null)
  424. member.StreamURL = presence["d"]["game"]["url"].ToString();
  425. }
  426. }
  427. }
  428. }
  429. }
  430. temp.Region = j["region"].ToString();
  431. temp.Owner = temp.GetMemberByKey(j["owner_id"].ToString());
  432. ServersList.Add(temp);
  433. }
  434. if (PrivateChannels == null)
  435. PrivateChannels = new List<DiscordPrivateChannel>();
  436. foreach (var privateChannel in m["d"]["private_channels"])
  437. {
  438. DiscordPrivateChannel tempPrivate = JsonConvert.DeserializeObject<DiscordPrivateChannel>(privateChannel.ToString());
  439. tempPrivate.Client = this;
  440. tempPrivate.user_id = privateChannel["recipient"]["id"].ToString();
  441. DiscordServer potentialServer = new DiscordServer();
  442. ServersList.ForEach(x =>
  443. {
  444. if (x.GetMemberByKey(privateChannel["recipient"]["id"].ToString()) != null)
  445. {
  446. potentialServer = x;
  447. }
  448. });
  449. if (potentialServer.Owner != null) //should be a safe test..i hope
  450. {
  451. DiscordMember recipient = potentialServer.GetMemberByKey(privateChannel["recipient"]["id"].ToString());
  452. if (recipient != null)
  453. {
  454. tempPrivate.Recipient = recipient;
  455. }
  456. else
  457. {
  458. DebugLogger.Log("Recipient was null!!!!", MessageLevel.Critical);
  459. }
  460. }
  461. else
  462. {
  463. DebugLogger.Log("No potential server found for user's private channel null! This will probably fix itself.", MessageLevel.Debug);
  464. }
  465. PrivateChannels.Add(tempPrivate);
  466. }
  467.  
  468. }
  469.  
  470. /// <summary>
  471. /// Sends an http DELETE request to leave the server you send in this parameter.
  472. /// </summary>
  473. /// <param name="server">The DiscordServer object you want to leave.</param>
  474. public void LeaveServer(DiscordServer server) => LeaveServer(server.ID);
  475.  
  476. /// <summary>
  477. /// (Owner only, non-bot only) Sends an http DELETE request to delete the server you specify.
  478. /// </summary>
  479. /// <param name="server">The DiscordServer object you want to delete.</param>
  480. public void DeleteServer(DiscordServer server) => DeleteServer(server.ID);
  481.  
  482. /// <summary>
  483. /// (Owner only, non-bot only) Sends an http DELETE request to delete the server you specify.
  484. /// </summary>
  485. /// <param name="ServerID">The server's ID you want to delete.</param>
  486. public void LeaveServer(string ServerID)
  487. {
  488. string url = //Endpoints.BaseAPI + Endpoints.Guilds + $"/{ServerID}";
  489. Endpoints.BaseAPI + Endpoints.Users + Endpoints.Me + Endpoints.Guilds + $"/{ServerID}"; //old, left for lulz
  490. try
  491. {
  492. WebWrapper.Delete(url, token);
  493. }
  494. catch (Exception ex)
  495. {
  496. DebugLogger.Log($"Error ocurred while leaving server ({ServerID}): {ex.Message}", MessageLevel.Error);
  497. }
  498. }
  499.  
  500. /// <summary>
  501. /// (Owner only, non-bot only) Sends an http DELETE request to delete the server you specify by ID.
  502. /// </summary>
  503. /// <param name="ServerID">The server's ID you want to delete.</param>
  504. public void DeleteServer(string ServerID)
  505. {
  506. if (IsBotAccount)
  507. throw new Exception("Bot accounts can't own servers!");
  508.  
  509. string url = Endpoints.BaseAPI + Endpoints.Guilds + $"/{ServerID}";
  510. try
  511. {
  512. WebWrapper.Delete(url, token);
  513. }
  514. catch (Exception ex)
  515. {
  516. DebugLogger.Log($"Error ocurred while deleting server ({ServerID}): {ex.Message}", MessageLevel.Error);
  517. }
  518. }
  519.  
  520.  
  521. /// <summary>
  522. /// Sends a message to a channel, what else did you expect?
  523. /// </summary>
  524. /// <param name="message">The text to send</param>
  525. /// <param name="channel">DiscordChannel object to send the message to.</param>
  526. /// <returns>A DiscordMessage object of the message sent to Discord.</returns>
  527. public DiscordMessage SendMessageToChannel(string message, DiscordChannel channel)
  528. {
  529. string url = Endpoints.BaseAPI + Endpoints.Channels + $"/{channel.ID}" + Endpoints.Messages;
  530. try
  531. {
  532. JObject result = JObject.Parse(WebWrapper.Post(url, token, JsonConvert.SerializeObject(Utils.GenerateMessage(message))));
  533. if (result["content"].IsNullOrEmpty())
  534. throw new InvalidOperationException("Request returned a blank message, you may not have permission to send messages yet!");
  535.  
  536. DiscordMessage m = new DiscordMessage
  537. {
  538. ID = result["id"].ToString(),
  539. Attachments = result["attachments"].ToObject<DiscordAttachment[]>(),
  540. Author = channel.Parent.GetMemberByKey(result["author"]["id"].ToString()),
  541. channel = channel,
  542. TypeOfChannelObject = channel.GetType(),
  543. Content = result["content"].ToString(),
  544. RawJson = result,
  545. timestamp = result["timestamp"].ToObject<DateTime>()
  546. };
  547. return m;
  548. }
  549. catch (Exception ex)
  550. {
  551. DebugLogger.Log($"Error ocurred while sending message to channel ({channel.Name}): {ex.Message}", MessageLevel.Error);
  552. }
  553. return null;
  554. }
  555.  
  556. /// <summary>
  557. /// Sends a file to the specified DiscordChannel with the given message.
  558. /// </summary>
  559. /// <param name="channel">The channel to send the message to.</param>
  560. /// <param name="message">The message you want the file to have with it.</param>
  561. /// <param name="pathToFile">The path to the file you wish to send (be careful!)</param>
  562. public void AttachFile(DiscordChannel channel, string message, string pathToFile)
  563. {
  564. string url = Endpoints.BaseAPI + Endpoints.Channels + $"/{channel.ID}" + Endpoints.Messages;
  565. //WebWrapper.PostWithAttachment(url, message, pathToFile);
  566. try
  567. {
  568. var uploadResult = JObject.Parse(WebWrapper.HttpUploadFile(url, token, pathToFile, "file", "image/jpeg", null));
  569.  
  570. if (!string.IsNullOrEmpty(message))
  571. EditMessage(uploadResult["id"].ToString(), message, channel);
  572. }
  573. catch (Exception ex)
  574. {
  575. DebugLogger.Log($"Error ocurred while sending file ({pathToFile}) to {channel.Name}: {ex.Message}", MessageLevel.Error);
  576. }
  577. }
  578.  
  579. /// <summary>
  580. /// Sends a file to the specified DiscordChannel with the given message.
  581. /// </summary>
  582. /// <param name="channel">The channel to send the message to.</param>
  583. /// <param name="message">The message you want the file to have with it.</param>
  584. /// <param name="stream">A stream object to send the bytes from.</param>
  585. public void AttachFile(DiscordChannel channel, string message, System.IO.Stream stream)
  586. {
  587. string url = Endpoints.BaseAPI + Endpoints.Channels + $"/{channel.ID}" + Endpoints.Messages;
  588. //WebWrapper.PostWithAttachment(url, message, pathToFile);
  589. try
  590. {
  591. var uploadResult = JObject.Parse(WebWrapper.HttpUploadFile(url, token, stream, "file", "image/jpeg", null));
  592.  
  593. if (!string.IsNullOrEmpty(message))
  594. EditMessage(uploadResult["id"].ToString(), message, channel);
  595. }
  596. catch (Exception ex)
  597. {
  598. DebugLogger.Log($"Error ocurred while sending file by stream to {channel.Name}: {ex.Message}", MessageLevel.Error);
  599. }
  600. }
  601.  
  602. /// <summary>
  603. /// Changes the current client's avatar.
  604. /// Any high resolution pictures are automatically downscaled and Discord will perform jpeg compression on them.
  605. /// </summary>
  606. /// <param name="image">The Bitmap object assosciated with the avatar you wish to upload.</param>
  607. public void ChangeClientAvatar(Bitmap image)
  608. {
  609. string base64 = Convert.ToBase64String(Utils.ImageToByteArray(image));
  610. string type = "image/jpeg;base64";
  611. string req = $"data:{type},{base64}";
  612. string usernameRequestJson = JsonConvert.SerializeObject(new
  613. {
  614. avatar = req,
  615. email = ClientPrivateInformation.Email,
  616. password = ClientPrivateInformation.Password,
  617. username = ClientPrivateInformation.Username
  618. });
  619. string url = Endpoints.BaseAPI + Endpoints.Users + "/@me";
  620. try
  621. {
  622. WebWrapper.Patch(url, token, usernameRequestJson);
  623. }
  624. catch (Exception ex)
  625. {
  626. DebugLogger.Log($"Error ocurred while changing client's avatar: {ex.Message}", MessageLevel.Error);
  627. }
  628. }
  629.  
  630. /// <summary>
  631. /// Changes the icon assosciated with the guild. Discord will perform jpeg compression and this image is automatically downscaled.
  632. /// </summary>
  633. /// <param name="image">The bitmap object associated </param>
  634. /// <param name="guild">The guild of the icon you wish to change.</param>
  635. public void ChangeGuildIcon(Bitmap image, DiscordServer guild)
  636. {
  637. Bitmap resized = new Bitmap((Image)image, 200, 200);
  638.  
  639. string base64 = Convert.ToBase64String(Utils.ImageToByteArray(resized));
  640. string type = "image/jpeg;base64";
  641. string req = $"data:{type},{base64}";
  642. string guildjson = JsonConvert.SerializeObject(new { icon = req, name = guild.Name });
  643. string url = Endpoints.BaseAPI + Endpoints.Guilds + "/" + guild.ID;
  644. try
  645. {
  646. var result = JObject.Parse(WebWrapper.Patch(url, token, guildjson));
  647. }
  648. catch (Exception ex)
  649. {
  650. DebugLogger.Log($"Error ocurred while changing guild {guild.Name}'s icon: {ex.Message}", MessageLevel.Error);
  651. }
  652. }
  653.  
  654. /// <summary>
  655. /// Returns a List of DiscordMessages.
  656. /// </summary>
  657. /// <param name="channel">The channel to return them from.</param>
  658. /// <param name="count">How many to return</param>
  659. /// <param name="idBefore">Messages before this message ID.</param>
  660. /// <param name="idAfter">Messages after this message ID.</param>
  661. /// <returns>A List of DiscordMessages that you can iterate through.</returns>
  662. public List<DiscordMessage> GetMessageHistory(DiscordChannelBase channel, int count, string idBefore = "", string idAfter = "")
  663. {
  664. string request = "https://discordapp.com/api/channels/" + channel.ID + $"/messages?&limit={count}";
  665. if (!string.IsNullOrEmpty(idBefore))
  666. request += $"&before={idBefore}";
  667. if (string.IsNullOrEmpty(idAfter))
  668. request += $"&after={idAfter}";
  669.  
  670. JArray result = null;
  671.  
  672. try
  673. {
  674. string res = WebWrapper.Get(request, token);
  675. result = JArray.Parse(res);
  676. }
  677. catch (Exception ex)
  678. {
  679. DebugLogger.Log($"Error ocurred while getting message history for channel {channel.ID}: {ex.Message}", MessageLevel.Error);
  680. }
  681.  
  682. if (result != null)
  683. {
  684. List<DiscordMessage> messageList = new List<DiscordMessage>();
  685. /// NOTE
  686. /// For some reason, the d object is excluded from this.
  687. foreach (var item in result.Children())
  688. {
  689. messageList.Add(new DiscordMessage
  690. {
  691. ID = item["id"].ToString(),
  692. channel = channel,
  693. Attachments = item["attachments"].ToObject<DiscordAttachment[]>(),
  694. TypeOfChannelObject = channel.GetType(),
  695. Author = GetMemberFromChannel(channel, item["author"]["id"].ToString()),
  696. Content = item["content"].ToString(),
  697. RawJson = item.ToObject<JObject>(),
  698. timestamp = DateTime.Parse(item["timestamp"].ToString())
  699. });
  700. }
  701. return messageList;
  702. }
  703.  
  704. return null;
  705. }
  706.  
  707. /// <summary>
  708. /// Changes the channel topic assosciated with the Discord text channel.
  709. /// </summary>
  710. /// <param name="Channeltopic">The new channel topic.</param>
  711. /// <param name="channel">The channel you wish to change the topic for.</param>
  712. public void ChangeChannelTopic(string Channeltopic, DiscordChannel channel)
  713. {
  714. string topicChangeJson = JsonConvert.SerializeObject(
  715. new
  716. {
  717. name = channel.Name,
  718. topic = Channeltopic
  719. });
  720. string url = Endpoints.BaseAPI + Endpoints.Channels + $"/{channel.ID}";
  721. try
  722. {
  723. var result = JObject.Parse(WebWrapper.Patch(url, token, topicChangeJson));
  724. ServersList.Find(x => x.Channels.Find(y => y.ID == channel.ID) != null).Channels.Find(x => x.ID == channel.ID).Topic = Channeltopic;
  725. }
  726. catch (Exception ex)
  727. {
  728. DebugLogger.Log($"Error ocurred while changing channel topic for channel {channel.Name}: {ex.Message}", MessageLevel.Error);
  729. }
  730. }
  731.  
  732. /*
  733. public List<DiscordRole> GetRoles(DiscordServer server)
  734. {
  735. return null;
  736. }
  737. */
  738.  
  739. /// <summary>
  740. /// Used for changing the client's email, password, username, etc.
  741. /// </summary>
  742. /// <param name="info"></param>
  743. public void ChangeClientInformation(DiscordUserInformation info)
  744. {
  745. string usernameRequestJson;
  746. if (info.Password != ClientPrivateInformation.Password)
  747. {
  748. usernameRequestJson = JsonConvert.SerializeObject(new
  749. {
  750. email = info.Email,
  751. new_password = info.Password,
  752. password = ClientPrivateInformation.Password,
  753. username = info.Username,
  754. avatar = info.Avatar
  755. });
  756. ClientPrivateInformation.Password = info.Password;
  757. try
  758. {
  759. File.Delete("token_cache");
  760. DebugLogger.Log("Deleted token_cache due to change of password.");
  761. }
  762. catch (Exception) { /*ignore*/ }
  763. }
  764. else
  765. {
  766. usernameRequestJson = JsonConvert.SerializeObject(new
  767. {
  768. email = info.Email,
  769. password = info.Password,
  770. username = info.Username,
  771. avatar = info.Avatar
  772. });
  773. }
  774.  
  775. string url = Endpoints.BaseAPI + Endpoints.Users + "/@me";
  776. try
  777. {
  778. var result = JObject.Parse(WebWrapper.Patch(url, token, usernameRequestJson));
  779. foreach (var server in ServersList)
  780. {
  781. if (server.Members[Me.ID] != null)
  782. server.Members[Me.ID].Username = info.Username;
  783. }
  784. Me.Username = info.Username;
  785. Me.Email = info.Email;
  786. Me.Avatar = info.Avatar;
  787. }
  788. catch (Exception ex)
  789. {
  790. DebugLogger.Log($"Error ocurred while changing client's information: {ex.Message}", MessageLevel.Error);
  791. }
  792. }
  793.  
  794. private void ChangeClientUsername(string newUsername)
  795. {
  796. string url = Endpoints.BaseAPI + Endpoints.Users + "/@me";
  797. string usernameRequestJson = JsonConvert.SerializeObject(new
  798. {
  799. email = ClientPrivateInformation.Email,
  800. password = ClientPrivateInformation.Password,
  801. username = newUsername,
  802. avatar = Me.Avatar,
  803. });
  804. try
  805. {
  806. var result = JObject.Parse(WebWrapper.Patch(url, token, usernameRequestJson));
  807. if (result != null)
  808. {
  809. foreach (var server in ServersList)
  810. {
  811. if (server.Members[Me.ID] != null)
  812. server.Members[Me.ID].Username = newUsername;
  813. }
  814. Me.Username = newUsername;
  815. }
  816. }
  817. catch (Exception ex)
  818. {
  819. DebugLogger.Log($"Error ocurred while changing client's username: {ex.Message}", MessageLevel.Error);
  820. }
  821. }
  822.  
  823. /// <summary>
  824. /// Sends a private message to the given user.
  825. /// </summary>
  826. /// <param name="message">The message text to send them.</param>
  827. /// <param name="member">The member you want to send this to.</param>
  828. /// <returns></returns>
  829. public DiscordMessage SendMessageToUser(string message, DiscordMember member)
  830. {
  831. string url = Endpoints.BaseAPI + Endpoints.Users + $"/{Me.ID}" + Endpoints.Channels;
  832. string initMessage = "{\"recipient_id\":" + member.ID + "}";
  833.  
  834. try
  835. {
  836. var result = JObject.Parse(WebWrapper.Post(url, token, initMessage));
  837. if (result != null)
  838. {
  839. DiscordMember recipient = ServersList.Find(
  840. x => x.GetMemberByKey(result["recipient"]["id"].ToString()) != null).Members[result["recipient"]["id"].ToString()];
  841. return SendActualMessage(result["id"].ToString(), message, recipient);
  842. }
  843. }
  844. catch (Exception ex)
  845. {
  846. DebugLogger.Log($"Error ocurred while sending message to user, step 1: {ex.Message}", MessageLevel.Error);
  847. }
  848.  
  849. return null;
  850. }
  851.  
  852. private DiscordMessage SendActualMessage(string id, string message, DiscordMember recipient)
  853. {
  854. string url = Endpoints.BaseAPI + Endpoints.Channels + $"/{id}" + Endpoints.Messages;
  855. DiscordMessage toSend = Utils.GenerateMessage(message);
  856.  
  857. try
  858. {
  859. var result = JObject.Parse(WebWrapper.Post(url, token, JsonConvert.SerializeObject(toSend).ToString()));
  860. DiscordMessage d = JsonConvert.DeserializeObject<DiscordMessage>(result.ToString());
  861. d.Recipient = recipient;
  862. d.channel = PrivateChannels.Find(x => x.ID == result["channel_id"].ToString());
  863. d.TypeOfChannelObject = typeof(DiscordPrivateChannel);
  864. d.Author = Me;
  865. return d;
  866. }
  867. catch (Exception ex)
  868. {
  869. DebugLogger.Log($"Error ocurred while sending message to user, step 2: {ex.Message}", MessageLevel.Error);
  870. }
  871. return null;
  872. }
  873.  
  874. /// <summary>
  875. /// Gets the string value of the current game your bot is 'playing'.
  876. /// </summary>
  877. public string GetCurrentGame => CurrentGameName;
  878.  
  879. /// <summary>
  880. /// Returns true if the websocket is not null and is alive.
  881. /// </summary>
  882. public bool WebsocketAlive => (ws != null) ? ws.IsAlive : false;
  883.  
  884. public bool ReadyComplete { get; private set; }
  885.  
  886. #region Message Received Crap..
  887.  
  888. /// <summary>
  889. /// Updates the bot's 'Currently playing' status to the following text. Pass in null if you want to remove this.
  890. /// </summary>
  891. /// <param name="gameName">The game's name. Old gameid lookup can be seen at: http://hastebin.com/azijiyaboc.json/ </param>
  892. /// <param name="streaming">Whether or not you want your bot to appear as if it is streaming. True means it will show it's streaming.</param>
  893. /// <param name="url">The 'url' for the stream, if your bot is streaming.</param>
  894. public void UpdateCurrentGame(string gameName, bool streaming, string url = null)
  895. {
  896. string msg;
  897. if (gameName.ToLower().Trim() != "")
  898. {
  899. msg = JsonConvert.SerializeObject(
  900. new
  901. {
  902. op = 3,
  903. d = new
  904. {
  905. idle_since = IdleSinceUnixTime == null ? (object)null : IdleSinceUnixTime,
  906. game = new
  907. {
  908. name = gameName,
  909. type = streaming ? 1 : 0,
  910. url = (url != null) ? url : (object)null
  911. }
  912. }
  913. });
  914. CurrentGameName = gameName;
  915. DebugLogger.Log($"Updating client's current game as '{gameName}'");
  916. }
  917. else
  918. {
  919. msg = JsonConvert.SerializeObject(
  920. new
  921. {
  922. op = 3,
  923. d = new
  924. {
  925. idle_since = IdleSinceUnixTime == null ? (object)null : IdleSinceUnixTime,
  926. game = (object)null
  927. }
  928. });
  929. DebugLogger.Log("Setting current game to null.");
  930. }
  931. ws.Send(msg.ToString());
  932. }
  933.  
  934. /// <summary>
  935. /// Updates the bot's status.
  936. /// </summary>
  937. /// <param name="idle">True if you want the bot to report as idle.</param>
  938. public void UpdateBotStatus(bool idle)
  939. {
  940. string msg;
  941. msg = JsonConvert.SerializeObject(
  942. new
  943. {
  944. op = 3,
  945. d = new
  946. {
  947. idle_since = idle ? (int)(DateTime.UtcNow - epoch).TotalMilliseconds : (object)null,
  948. game = CurrentGameName.ToLower().Trim() == "" ? (object)null : new { name = CurrentGameName }
  949. }
  950. });
  951. ws.Send(msg.ToString()); //let's try it!
  952. }
  953.  
  954. private void PresenceUpdateEvents(JObject message)
  955. {
  956. DiscordPresenceUpdateEventArgs dpuea = new DiscordPresenceUpdateEventArgs();
  957. dpuea.RawJson = message;
  958.  
  959. if (!message["d"]["guild_id"].IsNullOrEmpty())
  960. {
  961. var server = ServersList.Find(x => x.ID == message["d"]["guild_id"].ToString());
  962. if (server != null)
  963. {
  964. var user = server.GetMemberByKey(message["d"]["user"]["id"].ToString().Trim());
  965. if (user != null)
  966. {
  967. //If usernames change.
  968. if (!message["d"]["user"]["username"].IsNullOrEmpty())
  969. user.Username = message["d"]["user"]["username"].ToString();
  970.  
  971. //If avatar changes.
  972. if (!message["d"]["user"]["avatar"].IsNullOrEmpty())
  973. user.Avatar = message["d"]["user"]["avatar"].ToString();
  974.  
  975. if (message["d"]["nick"].ToString() == null)
  976. user.Nickname = null;
  977. else
  978. user.Nickname = message["d"]["nick"].ToString();
  979.  
  980. //Actual presence update
  981. user.SetPresence(message["d"]["status"].ToString());
  982.  
  983. //Updating games.
  984. string game = message["d"]["game"].ToString();
  985. if (message["d"]["game"].IsNullOrEmpty()) //null means not playing
  986. {
  987. dpuea.Game = "";
  988. user.CurrentGame = null;
  989. }
  990. else
  991. {
  992. if (message["d"]["game"]["name"].IsNullOrEmpty())
  993. if (message["d"]["game"]["game"].IsNullOrEmpty())
  994. dpuea.Game = "";
  995. else
  996. dpuea.Game = message["d"]["game"]["game"].ToString();
  997. else
  998. dpuea.Game = message["d"]["game"]["name"].ToString();
  999. user.CurrentGame = dpuea.Game;
  1000.  
  1001. if (message["d"]["game"]["type"] != null && message["d"]["game"]["type"].ToObject<int>() == 1)
  1002. {
  1003. user.Streaming = true;
  1004. if (message["d"]["game"]["url"].ToString() != null)
  1005. user.StreamURL = message["d"]["game"]["url"].ToString();
  1006. }
  1007. }
  1008. dpuea.User = user;
  1009.  
  1010. if (message["d"]["status"].ToString() == "online")
  1011. dpuea.Status = DiscordUserStatus.ONLINE;
  1012. else if (message["d"]["status"].ToString() == "idle")
  1013. dpuea.Status = DiscordUserStatus.IDLE;
  1014. else if (message["d"]["status"].ToString() == null || message["d"]["status"].ToString() == "offline")
  1015. dpuea.Status = DiscordUserStatus.OFFLINE;
  1016. if (PresenceUpdated != null)
  1017. PresenceUpdated(this, dpuea);
  1018. }
  1019. else
  1020. {
  1021. if (!message["d"]["guild_id"].IsNullOrEmpty()) //if this is null or empty, that means this pertains to friends list
  1022. {
  1023. if (!message["d"]["user"]["username"].IsNullOrEmpty() && !message["d"]["user"]["id"].IsNullOrEmpty())
  1024. {
  1025. DebugLogger.Log($"User {message["d"]["user"]["username"]} ({message["d"]["user"]["id"].ToString()}) doesn't exist in server {server.Name} ({server.ID}) no problemo. Creating/adding", MessageLevel.Debug);
  1026. DiscordMember memeber = JsonConvert.DeserializeObject<DiscordMember>(message["d"]["user"].ToString());
  1027. memeber.parentclient = this;
  1028. memeber.SetPresence(message["d"]["status"].ToString());
  1029. memeber.Parent = ServersList.Find(x => x.ID == message["d"]["guild_id"].ToString());
  1030.  
  1031. if (message["d"]["game"].IsNullOrEmpty())
  1032. {
  1033. dpuea.Game = "";
  1034. memeber.CurrentGame = null;
  1035. }
  1036. else
  1037. {
  1038. dpuea.Game = message["d"]["game"]["name"].ToString();
  1039. memeber.CurrentGame = dpuea.Game;
  1040. if (message["d"]["game"]["type"].ToObject<int>() == 1)
  1041. {
  1042. user.Streaming = true;
  1043. if (message["d"]["game"]["url"].ToString() != null)
  1044. user.StreamURL = message["d"]["game"]["url"].ToString();
  1045. }
  1046. }
  1047.  
  1048. if (message["d"]["status"].ToString() == "online")
  1049. dpuea.Status = DiscordUserStatus.ONLINE;
  1050. else if (message["d"]["status"].ToString() == "idle")
  1051. dpuea.Status = DiscordUserStatus.IDLE;
  1052. else if (message["d"]["status"].ToString() == null || message["d"]["status"].ToString() == "offline")
  1053. dpuea.Status = DiscordUserStatus.OFFLINE;
  1054.  
  1055. memeber.Parent.AddMember(memeber);
  1056. }
  1057. }
  1058. }
  1059. }
  1060. }
  1061. }
  1062.  
  1063.  
  1064. /// <summary>
  1065. /// Deletes a message with a specified ID.
  1066. /// This method will only work if the message was sent since the bot has ran.
  1067. /// </summary>
  1068. /// <param name="id"></param>
  1069. public void DeleteMessage(string id)
  1070. {
  1071. var message = MessageLog[id];
  1072. if (message != null)
  1073. SendDeleteRequest(message);
  1074. }
  1075.  
  1076. /// <summary>
  1077. /// Deletes a specified DiscordMessage.
  1078. /// </summary>
  1079. /// <param name="message"></param>
  1080. public void DeleteMessage(DiscordMessage message)
  1081. {
  1082. SendDeleteRequest(message);
  1083. }
  1084.  
  1085. //public void DeletePrivateMessage(DiscordMessage message)
  1086. //{
  1087. // SendDeleteRequest(message, true);
  1088. //}
  1089.  
  1090. /// <summary>
  1091. /// Deletes all messages made by the bot since running.
  1092. /// </summary>
  1093. /// <returns>A count of messages deleted.</returns>
  1094. public int DeleteAllMessages()
  1095. {
  1096. int count = 0;
  1097.  
  1098. foreach (var kvp in MessageLog)
  1099. {
  1100. if (kvp.Value.Author.ID == Me.ID)
  1101. {
  1102. SendDeleteRequest(kvp.Value);
  1103. count++;
  1104. }
  1105. }
  1106. return count;
  1107. }
  1108.  
  1109. /// <summary>
  1110. /// Deletes the specified number of messages in a given channel.
  1111. /// Thank you to Siegen for this idea/method!
  1112. /// </summary>
  1113. /// <param name="channel">The channel to delete messages in.</param>
  1114. /// <param name="count">The amount of messages to delete (max 100)</param>
  1115. /// <returns>The count of messages deleted.</returns>
  1116. public int DeleteMultipleMessagesInChannel(DiscordChannel channel, int count)
  1117. {
  1118. if (count > 100)
  1119. count = 100;
  1120.  
  1121. int __count = 0;
  1122.  
  1123. var messages = GetMessageHistory(channel, count, null, null);
  1124.  
  1125. messages.ForEach(x =>
  1126. {
  1127. if (x.channel.ID == channel.ID)
  1128. {
  1129. SendDeleteRequest(x);
  1130. __count++;
  1131. }
  1132. });
  1133.  
  1134. return __count;
  1135. }
  1136.  
  1137. /// <summary>
  1138. ///
  1139. /// </summary>
  1140. /// <param name="channel"></param>
  1141. /// <param name="username"></param>
  1142. /// <param name="caseSensitive"></param>
  1143. /// <returns></returns>
  1144. public DiscordMember GetMemberFromChannel(DiscordChannelBase channel, string username, bool caseSensitive)
  1145. {
  1146. if (string.IsNullOrEmpty(username))
  1147. throw new ArgumentException("Argument given for username was null/empty.");
  1148. if (channel != null)
  1149. {
  1150. if (channel.GetType() == typeof(DiscordChannel)) //regular channel
  1151. {
  1152. DiscordMember foundMember = ((DiscordChannel)channel).Parent.GetMemberByUsername(username, caseSensitive);
  1153. if (foundMember != null)
  1154. {
  1155. return foundMember;
  1156. }
  1157. else
  1158. {
  1159. DebugLogger.Log("Error in GetMemberFromChannel: foundMember was null!", MessageLevel.Error);
  1160. }
  1161. }
  1162. else if(channel.GetType() == typeof(DiscordPrivateChannel))
  1163. {
  1164. return ((DiscordPrivateChannel)channel).Recipient;
  1165. }
  1166. }
  1167. else
  1168. {
  1169. DebugLogger.Log("Error in GetMemberFromChannel: channel was null!", MessageLevel.Error);
  1170. }
  1171. return null;
  1172. }
  1173.  
  1174. /// <summary>
  1175. ///
  1176. /// </summary>
  1177. /// <param name="channel"></param>
  1178. /// <param name="id"></param>
  1179. /// <returns></returns>
  1180. public DiscordMember GetMemberFromChannel(DiscordChannelBase channel, string id)
  1181. {
  1182. if (channel != null)
  1183. {
  1184. if (channel.GetType() == typeof(DiscordChannel)) //regular
  1185. {
  1186.  
  1187. DiscordMember foundMember = ((DiscordChannel)channel).Parent.GetMemberByKey(id);
  1188. if (foundMember != null)
  1189. return foundMember;
  1190. else
  1191. {
  1192. DebugLogger.Log($"Error in GetMemberFromChannel: foundMember was null! ID: {id}", MessageLevel.Error);
  1193. }
  1194. }
  1195. else if(channel.GetType() == typeof(DiscordPrivateChannel))
  1196. {
  1197. return ((DiscordPrivateChannel)channel).Recipient;
  1198. }
  1199. }
  1200. else
  1201. {
  1202. DebugLogger.Log("Error in GetMemberFromChannel: channel was null!", MessageLevel.Error);
  1203. }
  1204. return null;
  1205. }
  1206.  
  1207. /// <summary>
  1208. /// you probably shouldn't use this.
  1209. /// </summary>
  1210. /// <param name="channelName"></param>
  1211. /// <returns></returns>
  1212. public DiscordChannel GetChannelByName(string channelName)
  1213. {
  1214. try
  1215. {
  1216. return ServersList.Find(x => x.Channels.Find(y => y.Name.ToLower() == channelName.ToLower()) != null).Channels.Find(x => x.Name.ToLower() == channelName.ToLower());
  1217. }
  1218. catch
  1219. {
  1220. return null;
  1221. }
  1222. }
  1223.  
  1224. /// <summary>
  1225. ///
  1226. /// </summary>
  1227. /// <param name="id"></param>
  1228. /// <returns></returns>
  1229. public DiscordChannel GetChannelByID(long id)
  1230. {
  1231. return ServersList.Find(x => x.Channels.Find(y => y.ID == id.ToString()) != null).Channels.Find(z => z.ID == id.ToString());
  1232. }
  1233.  
  1234. /// <summary>
  1235. /// (Client account only) accepts an invite to a server.
  1236. /// </summary>
  1237. /// <param name="inviteID">The ID of the invite you want to accept. This is NOT the full URL of the invite</param>
  1238. public void AcceptInvite(string inviteID)
  1239. {
  1240. if (!IsBotAccount)
  1241. {
  1242. if (inviteID.StartsWith("http://"))
  1243. inviteID = inviteID.Substring(inviteID.LastIndexOf('/') + 1);
  1244.  
  1245. string url = Endpoints.BaseAPI + Endpoints.Invite + $"/{inviteID}";
  1246. try
  1247. {
  1248. var result = WebWrapper.Post(url, token, "", true);
  1249. DebugLogger.Log("Accept invite result: " + result.ToString());
  1250. }
  1251. catch (Exception ex)
  1252. {
  1253. DebugLogger.Log($"Error accepting invite: {ex.Message}", MessageLevel.Error);
  1254. }
  1255. }
  1256. else
  1257. throw new InvalidOperationException("Bot accounts can't accept invites normally! Please use the OAuth flow to add bots to servers you have the \"Manage Server\" permission in.");
  1258. }
  1259.  
  1260. /// <summary>
  1261. ///
  1262. /// </summary>
  1263. /// <returns>The last DiscordMessage sent</returns>
  1264. public DiscordMessage GetLastMessageSent()
  1265. {
  1266. foreach (var message in MessageLog)
  1267. {
  1268. if (message.Value.Author.ID == Me.ID)
  1269. {
  1270. return message.Value;
  1271. }
  1272. }
  1273. return null;
  1274. }
  1275.  
  1276. /// <summary>
  1277. ///
  1278. /// </summary>
  1279. /// <param name="inChannel"></param>
  1280. /// <returns>The last DiscordMessage sent in the given channel</returns>
  1281. public DiscordMessage GetLastMessageSent(DiscordChannel inChannel)
  1282. {
  1283. foreach (var message in MessageLog)
  1284. {
  1285. if (message.Value.Author.ID == Me.ID && message.Value.channel.ID == inChannel.ID)
  1286. {
  1287. return message.Value;
  1288. }
  1289. }
  1290. return null;
  1291. }
  1292.  
  1293. /// <summary>
  1294. /// If you screwed up, you can use this method to edit a given message. This sends out an http patch request with a replacement message
  1295. /// </summary>
  1296. /// <param name="MessageID">The ID of the message you want to edit.</param>
  1297. /// <param name="replacementMessage">What you want the text to be edited to.</param>
  1298. /// <param name="channel">The channel the message is in</param>
  1299. /// <returns>the new and improved DiscordMessage object.</returns>
  1300. public DiscordMessage EditMessage(string MessageID, string replacementMessage, DiscordChannel channel)
  1301. {
  1302. string url = Endpoints.BaseAPI + Endpoints.Channels + $"/{channel.ID}" + Endpoints.Messages + $"/{MessageID}";
  1303. try
  1304. {
  1305. string replacement = JsonConvert.SerializeObject(
  1306. new
  1307. {
  1308. content = replacementMessage,
  1309. mentions = new string[0]
  1310. }
  1311. );
  1312. JObject result = JObject.Parse(WebWrapper.Patch(url, token, replacement));
  1313.  
  1314. DiscordMessage m = new DiscordMessage
  1315. {
  1316. RawJson = result,
  1317. Attachments = result["attachments"].ToObject<DiscordAttachment[]>(),
  1318. Author = channel.Parent.GetMemberByKey(result["author"]["id"].ToString()),
  1319. TypeOfChannelObject = channel.GetType(),
  1320. channel = channel,
  1321. Content = result["content"].ToString(),
  1322. ID = result["id"].ToString(),
  1323. timestamp = result["timestamp"].ToObject<DateTime>()
  1324. };
  1325. return m;
  1326. }
  1327. catch (Exception ex)
  1328. {
  1329. DebugLogger.Log("Exception ocurred while editing: " + ex.Message, MessageLevel.Error);
  1330. }
  1331.  
  1332. return null;
  1333. }
  1334.  
  1335. private void SendDeleteRequest(DiscordMessage message)
  1336. {
  1337. string url;
  1338. //if(!user)
  1339. url = Endpoints.BaseAPI + Endpoints.Channels + $"/{message.channel.ID}" + Endpoints.Messages + $"/{message.ID}";
  1340. //else
  1341. //url = Endpoints.BaseAPI + Endpoints.Channels + $"/{message.channel.id}" + Endpoints.Messages + $"/{message.id}";
  1342. try
  1343. {
  1344. var result = WebWrapper.Delete(url, token);
  1345. }
  1346. catch (Exception ex)
  1347. {
  1348. DebugLogger.Log($"Exception ocurred while deleting message (ID: {message.ID}): " + ex.Message, MessageLevel.Error);
  1349. }
  1350. }
  1351.  
  1352. private DiscordMessage FindInMessageLog(ID id)
  1353. {
  1354. foreach (var message in MessageLog)
  1355. if (message.Key == id)
  1356. return message.Value;
  1357.  
  1358. return null;
  1359. }
  1360.  
  1361. private void MessageUpdateEvents(JObject message)
  1362. {
  1363. try
  1364. {
  1365. DiscordServer pserver = ServersList.Find(x => x.Channels.Find(y => y.ID == message["d"]["channel_id"].ToString()) != null);
  1366. DiscordChannel pchannel = pserver.Channels.Find(x => x.ID == message["d"]["channel_id"].ToString());
  1367. if (pchannel != null)
  1368. {
  1369. if (message["d"]["author"] != null)
  1370. {
  1371. var toRemove = FindInMessageLog(message["d"]["id"].ToString());
  1372. if (toRemove == null)
  1373. return; //No message exists
  1374. var jsonToEdit = toRemove.RawJson;
  1375. jsonToEdit["d"]["content"].Replace(JToken.FromObject(message["d"]["content"].ToString()));
  1376. if (MessageEdited != null)
  1377. MessageEdited(this, new DiscordMessageEditedEventArgs
  1378. {
  1379. Author = pserver.GetMemberByKey(message["d"]["author"]["id"].ToString()),
  1380. Channel = pchannel,
  1381. MessageText = message["d"]["content"].ToString(),
  1382. MessageType = DiscordMessageType.CHANNEL,
  1383. MessageEdited = new DiscordMessage
  1384. {
  1385. Author = pserver.GetMemberByKey(message["d"]["author"]["id"].ToString()),
  1386. Content = toRemove.Content,
  1387. Attachments = message["d"]["attachments"].ToObject<DiscordAttachment[]>(),
  1388. channel = pserver.Channels.Find(x => x.ID == message["d"]["channel_id"].ToString()),
  1389. RawJson = message,
  1390. ID = message["d"]["id"].ToString(),
  1391. timestamp = message["d"]["timestamp"].ToObject<DateTime>(),
  1392. },
  1393. EditedTimestamp = message["d"]["edited_timestamp"].ToObject<DateTime>()
  1394. });
  1395. MessageLog.Remove(message["d"]["id"].ToString());
  1396.  
  1397. DiscordMessage newMessage = toRemove;
  1398. newMessage.Content = jsonToEdit["d"]["content"].ToString();
  1399. MessageLog.Add(message["d"]["id"].ToString(), newMessage);
  1400.  
  1401. }
  1402. else //I know they say assume makes an ass out of you and me...but we're assuming it's Discord's weird auto edit of a just URL message
  1403. {
  1404. if (URLMessageAutoUpdate != null)
  1405. {
  1406. DiscordURLUpdateEventArgs asdf = new DiscordURLUpdateEventArgs(); //I'm running out of clever names and should probably split these off into different internal voids soon...
  1407. asdf.ID = message["d"]["id"].ToString();
  1408. asdf.Channel = ServersList.Find(x => x.Channels.Find(y => y.ID == message["d"]["channel_id"].ToString()) != null).Channels.Find(x => x.ID == message["d"]["channel_id"].ToString());
  1409. foreach (var embed in message["d"]["embeds"])
  1410. {
  1411. DiscordEmbeds temp = new DiscordEmbeds();
  1412. temp.URL = embed["url"].ToString();
  1413. temp.Description = embed["description"].ToString();
  1414. try
  1415. {
  1416. temp.ProviderName = embed["provider"]["name"] == null ? null : embed["provider"]["name"].ToString();
  1417. temp.ProviderURL = embed["provider"]["url"].ToString();
  1418. }
  1419. catch { }//noprovider
  1420. temp.Title = embed["title"].ToString();
  1421. temp.Type = embed["type"].ToString();
  1422. asdf.Embeds.Add(temp);
  1423. }
  1424. URLMessageAutoUpdate(this, asdf);
  1425. }
  1426. }
  1427. }
  1428. else
  1429. {
  1430. DebugLogger.Log("Couldn't find channel!", MessageLevel.Critical);
  1431. }
  1432. }
  1433. catch (Exception ex)
  1434. {
  1435. DebugLogger.Log($"Exception during MessageUpdateEvents.\n\tMessage: {ex.Message}\n\tStack: {ex.StackTrace}", MessageLevel.Critical);
  1436. }
  1437. }
  1438.  
  1439. private DiscordChannel GetDiscordChannelByID(string id)
  1440. {
  1441. DiscordChannel returnVal = new DiscordChannel { ID = "-1" };
  1442. ServersList.ForEach(x =>
  1443. {
  1444. x.Channels.ForEach(y =>
  1445. {
  1446. if (y.ID == id)
  1447. returnVal = y;
  1448. });
  1449. });
  1450. if (returnVal.ID != "-1")
  1451. return returnVal;
  1452. else
  1453. return null;
  1454. }
  1455.  
  1456. private void MessageCreateEvents(JObject message)
  1457. {
  1458. //try
  1459. //{
  1460. string tempChannelID = message["d"]["channel_id"].ToString();
  1461.  
  1462. //DiscordServer foundServerChannel = ServersList.Find(x => x.channels.Find(y => y.id == tempChannelID) != null);
  1463. DiscordChannel potentialChannel = GetDiscordChannelByID(message["d"]["channel_id"].ToString());
  1464. if (potentialChannel == null) //private message create
  1465. {
  1466. if (message["d"]["author"]["id"].ToString() != Me.ID)
  1467. {
  1468. var foundPM = PrivateChannels.Find(x => x.ID == message["d"]["channel_id"].ToString());
  1469. DiscordPrivateMessageEventArgs dpmea = new DiscordPrivateMessageEventArgs();
  1470. dpmea.Channel = foundPM;
  1471. dpmea.Message = message["d"]["content"].ToString();
  1472. DiscordMember tempMember = new DiscordMember(this);
  1473. tempMember.Username = message["d"]["author"]["username"].ToString();
  1474. tempMember.ID = message["d"]["author"]["id"].ToString();
  1475. dpmea.Author = tempMember;
  1476. tempMember.parentclient = this;
  1477. dpmea.RawJson = message;
  1478.  
  1479. if (PrivateMessageReceived != null)
  1480. PrivateMessageReceived(this, dpmea);
  1481. }
  1482. else
  1483. {
  1484. //if (DebugMessageReceived != null)
  1485. // DebugMessageReceived(this, new DiscordDebugMessagesEventArgs { message = "Ignoring MESSAGE_CREATE for private channel for message sent from this client." });
  1486. }
  1487. }
  1488. else
  1489. {
  1490. DiscordMessageEventArgs dmea = new DiscordMessageEventArgs();
  1491. dmea.RawJson = message;
  1492. dmea.Channel = potentialChannel;
  1493.  
  1494. dmea.MessageText = message["d"]["content"].ToString();
  1495.  
  1496. DiscordMember tempMember = null;
  1497. tempMember = potentialChannel.Parent.GetMemberByKey(message["d"]["author"]["id"].ToString());
  1498. if (tempMember == null)
  1499. {
  1500. tempMember = JsonConvert.DeserializeObject<DiscordMember>(message["author"].ToString());
  1501. tempMember.parentclient = this;
  1502. tempMember.Parent = potentialChannel.Parent;
  1503.  
  1504. potentialChannel.Parent.AddMember(tempMember);
  1505. }
  1506.  
  1507. dmea.Author = tempMember;
  1508.  
  1509. DiscordMessage m = new DiscordMessage();
  1510. m.Author = dmea.Author;
  1511. m.channel = dmea.Channel;
  1512. m.TypeOfChannelObject = dmea.Channel.GetType();
  1513. m.Content = dmea.MessageText;
  1514. m.ID = message["d"]["id"].ToString();
  1515. m.RawJson = message;
  1516. m.timestamp = DateTime.Now;
  1517. dmea.Message = m;
  1518. if (!message["d"]["attachments"].IsNullOrEmpty())
  1519. {
  1520. List<DiscordAttachment> tempList = new List<DiscordAttachment>();
  1521. foreach (var attachment in message["d"]["attachments"])
  1522. {
  1523. tempList.Add(JsonConvert.DeserializeObject<DiscordAttachment>(attachment.ToString()));
  1524. }
  1525. m.Attachments = tempList.ToArray();
  1526. }
  1527.  
  1528. if (!message["d"]["mentions"].IsNullOrEmpty())
  1529. {
  1530. JArray mentionsAsArray = JArray.Parse(message["d"]["mentions"].ToString());
  1531. foreach (var mention in mentionsAsArray)
  1532. {
  1533. string id = mention["id"].ToString();
  1534. if (id.Equals(Me.ID))
  1535. {
  1536. if (MentionReceived != null)
  1537. MentionReceived(this, dmea);
  1538. }
  1539. }
  1540. }
  1541.  
  1542. KeyValuePair<string, DiscordMessage> toAdd = new KeyValuePair<string, DiscordMessage>(message["d"]["id"].ToString(), m);
  1543. MessageLog.Add(message["d"]["id"].ToString(), m);
  1544.  
  1545. if (MessageReceived != null)
  1546. MessageReceived(this, dmea);
  1547. }
  1548. //}
  1549. //catch (Exception ex)
  1550. //{
  1551. // DebugLogger.Log("Error ocurred during MessageCreateEvents: " + ex.Message, MessageLevel.Error);
  1552. //}
  1553. }
  1554.  
  1555. private void ChannelCreateEvents(JObject message)
  1556. {
  1557. if (message["d"]["is_private"].ToString().ToLower() == "false")
  1558. {
  1559. var foundServer = ServersList.Find(x => x.ID == message["d"]["guild_id"].ToString());
  1560. if (foundServer != null)
  1561. {
  1562. DiscordChannel tempChannel = new DiscordChannel();
  1563. tempChannel.Client = this;
  1564. tempChannel.Name = message["d"]["name"].ToString();
  1565. tempChannel.Type = message["d"]["type"].ToObject<ChannelType>();
  1566. if (tempChannel.Type == ChannelType.Voice && !message["d"]["bitrate"].IsNullOrEmpty())
  1567. tempChannel.Bitrate = message["d"]["bitrate"].ToObject<int>();
  1568.  
  1569. tempChannel.ID = message["d"]["id"].ToString();
  1570. tempChannel.Parent = foundServer;
  1571. foundServer.Channels.Add(tempChannel);
  1572. DiscordChannelCreateEventArgs fae = new DiscordChannelCreateEventArgs();
  1573. fae.ChannelCreated = tempChannel;
  1574. fae.ChannelType = DiscordChannelCreateType.CHANNEL;
  1575. if (ChannelCreated != null)
  1576. ChannelCreated(this, fae);
  1577. }
  1578. }
  1579. else
  1580. {
  1581. DiscordPrivateChannel tempPrivate = new DiscordPrivateChannel();
  1582. tempPrivate.Client = this;
  1583. tempPrivate.ID = message["d"]["id"].ToString();
  1584. DiscordMember recipient = ServersList.Find(x => x.GetMemberByKey(message["d"]["recipient"]["id"].ToString()) != null).GetMemberByKey(message["d"]["recipient"]["id"].ToString());
  1585. tempPrivate.Recipient = recipient;
  1586. PrivateChannels.Add(tempPrivate);
  1587. DiscordPrivateChannelEventArgs fak = new DiscordPrivateChannelEventArgs { ChannelType = DiscordChannelCreateType.PRIVATE, ChannelCreated = tempPrivate };
  1588. if (PrivateChannelCreated != null)
  1589. PrivateChannelCreated(this, fak);
  1590. }
  1591. }
  1592. #endregion
  1593. private string GetGatewayUrl()
  1594. {
  1595. if (token == null)
  1596. throw new NullReferenceException("token was null!");
  1597.  
  1598. //i'm ashamed of myself for this but i'm tired
  1599. tryAgain:
  1600. string url = Endpoints.BaseAPI + Endpoints.Gateway;
  1601. if (V4Testing)
  1602. url = "https://ptb.discordapp.com/api/gateway";
  1603. try
  1604. {
  1605. string gateway = JObject.Parse(WebWrapper.Get(url, token))["url"].ToString();
  1606. if (!string.IsNullOrEmpty(gateway))
  1607. {
  1608. return gateway + (V4Testing ? "?encoding=json&v=4" : "");
  1609. }
  1610. else
  1611. throw new NullReferenceException("Failed to retrieve Gateway urL!");
  1612. }
  1613. catch (UnauthorizedAccessException) //bad token
  1614. {
  1615. DebugLogger.Log("Got 401 from Discord. Token bad, deleting and retrying login...");
  1616. if (File.Exists(((uint)StrippedEmail.GetHashCode()) + ".cache"))
  1617. {
  1618. File.Delete(((uint)StrippedEmail.GetHashCode()) + ".cache");
  1619. }
  1620. SendLoginRequest();
  1621. goto tryAgain;
  1622. }
  1623. catch (Exception ex)
  1624. {
  1625. DebugLogger.Log("Exception ocurred while retrieving Gateway URL: " + ex.Message, MessageLevel.Error);
  1626. return null;
  1627. }
  1628. }
  1629.  
  1630. /// <summary>
  1631. ///
  1632. /// </summary>
  1633. /// <param name="channel"></param>
  1634. /// <returns></returns>
  1635. public DiscordServer GetServerChannelIsIn(DiscordChannel channel)
  1636. {
  1637. return ServersList.Find(x => x.Channels.Find(y => y.ID == channel.ID) != null);
  1638. }
  1639.  
  1640. /// <summary>
  1641. /// Deletes a specified Discord channel given you have the permission.
  1642. /// </summary>
  1643. /// <param name="channel">The DiscordChannel object to delete</param>
  1644. public void DeleteChannel(DiscordChannel channel)
  1645. {
  1646. string url = Endpoints.BaseAPI + Endpoints.Channels + $"/{channel.ID}";
  1647. try
  1648. {
  1649. WebWrapper.Delete(url, token);
  1650. }
  1651. catch (Exception ex)
  1652. {
  1653. DebugLogger.Log("Exception ocurred while deleting channel: " + ex.Message, MessageLevel.Error);
  1654. }
  1655. }
  1656.  
  1657. /// <summary>
  1658. /// Creates either a text or voice channel in a DiscordServer given a name. Given you have the permission of course.
  1659. /// </summary>
  1660. /// <param name="server">The server to create the channel in.</param>
  1661. /// <param name="ChannelName">The name of the channel (will automatically be lowercased if text)</param>
  1662. /// <param name="voice">True if you want the channel to be a voice channel.</param>
  1663. /// <returns>The newly created DiscordChannel</returns>
  1664. public DiscordChannel CreateChannel(DiscordServer server, string ChannelName, bool voice)
  1665. {
  1666. string url = Endpoints.BaseAPI + Endpoints.Guilds + $"/{server.ID}" + Endpoints.Channels;
  1667. var reqJson = JsonConvert.SerializeObject(new { name = ChannelName, type = voice ? "voice" : "text" });
  1668. try
  1669. {
  1670. var result = JObject.Parse(WebWrapper.Post(url, token, reqJson));
  1671. if (result != null)
  1672. {
  1673. DiscordChannel dc = new DiscordChannel
  1674. {
  1675. Client = this,
  1676. Name = result["name"].ToString(),
  1677. ID = result["id"].ToString(),
  1678. Type = result["type"].ToObject<ChannelType>(),
  1679. Private = result["is_private"].ToObject<bool>(),
  1680. };
  1681. if (!result["topic"].IsNullOrEmpty())
  1682. dc.Topic = result["topic"].ToString();
  1683. if (dc.Type == ChannelType.Voice && !result["bitrate"].IsNullOrEmpty())
  1684. dc.Bitrate = result["bitrate"].ToObject<int>();
  1685.  
  1686. server.Channels.Add(dc);
  1687. return dc;
  1688. }
  1689. }
  1690. catch (Exception ex)
  1691. {
  1692. DebugLogger.Log("Exception ocurred while creating channel: " + ex.Message, MessageLevel.Error);
  1693. }
  1694. return null;
  1695. }
  1696.  
  1697. /// <summary>
  1698. /// Creates an empty guild with only this client in it given the following name.
  1699. /// Unknown if works on bot accounts or not.
  1700. /// </summary>
  1701. /// <param name="GuildName">The name of the guild you wish to create.</param>
  1702. /// <returns>the created DiscordServer</returns>
  1703. public DiscordServer CreateGuild(string GuildName)
  1704. {
  1705. string createGuildUrl = Endpoints.BaseAPI + Endpoints.Guilds;
  1706. string req = JsonConvert.SerializeObject(new { name = GuildName });
  1707.  
  1708. try
  1709. {
  1710. var response = JObject.Parse(WebWrapper.Post(createGuildUrl, token, req));
  1711. if (response != null)
  1712. {
  1713. DiscordServer server = new DiscordServer();
  1714. server.JoinedAt = response["joined_at"].ToObject<DateTime>();
  1715. server.ID = response["id"].ToString();
  1716. server.Name = response["name"].ToString();
  1717. server.parentclient = this;
  1718.  
  1719. string channelGuildUrl = createGuildUrl + $"/{server.ID}" + Endpoints.Channels;
  1720. var channelRespone = JArray.Parse(WebWrapper.Get(channelGuildUrl, token));
  1721. foreach (var item in channelRespone.Children())
  1722. {
  1723. server.Channels.Add(new DiscordChannel
  1724. {
  1725. Client = this,
  1726. Name = item["name"].ToString(),
  1727. ID = item["id"].ToString(),
  1728. Topic = item["topic"].ToString(),
  1729. Private = item["is_private"].ToObject<bool>(),
  1730. Type = item["type"].ToObject<ChannelType>()
  1731. });
  1732. }
  1733.  
  1734. server.AddMember(Me);
  1735. server.Owner = server.GetMemberByKey(response["owner_id"].ToString());
  1736. if (server.Owner == null)
  1737. DebugLogger.Log("Owner is null in CreateGuild!", MessageLevel.Critical);
  1738.  
  1739. ServersList.Add(server);
  1740. return server;
  1741. }
  1742. }
  1743. catch (Exception ex)
  1744. {
  1745. DebugLogger.Log("Exception ocurred while creating guild: " + ex.Message, MessageLevel.Error);
  1746. }
  1747. return null;
  1748. }
  1749.  
  1750. /// <summary>
  1751. /// Edits the name of the guild, given you have the permission.
  1752. /// </summary>
  1753. /// <param name="guild">The guild's name you wish to edit.</param>
  1754. /// <param name="NewGuildName">The new guild name.</param>
  1755. public void EditGuildName(DiscordServer guild, string NewGuildName)
  1756. {
  1757. string editGuildUrl = Endpoints.BaseAPI + Endpoints.Guilds + $"/{guild.ID}";
  1758. var newNameJson = JsonConvert.SerializeObject(new { name = NewGuildName });
  1759. try
  1760. {
  1761. WebWrapper.Patch(editGuildUrl, token, newNameJson);
  1762. }
  1763. catch (Exception ex)
  1764. {
  1765. DebugLogger.Log($"Exception ocurred while editing guild ({guild.Name}) name: " + ex.Message, MessageLevel.Error);
  1766. }
  1767. }
  1768.  
  1769. /// <summary>
  1770. /// Assigns a specified role to a member, given you have the permission.
  1771. /// </summary>
  1772. /// <param name="guild">The guild you and the user are in.</param>
  1773. /// <param name="role">The role you wish to assign them.</param>
  1774. /// <param name="member">The member you wish to assign the role to.</param>
  1775. public void AssignRoleToMember(DiscordServer guild, DiscordRole role, DiscordMember member)
  1776. {
  1777. string url = Endpoints.BaseAPI + Endpoints.Guilds + $"/{guild.ID}" + Endpoints.Members + $"/{member.ID}";
  1778. string message = JsonConvert.SerializeObject(new { roles = new string[] { role.ID } });
  1779. try
  1780. {
  1781. WebWrapper.Patch(url, token, message);
  1782. }
  1783. catch (Exception ex)
  1784. {
  1785. DebugLogger.Log($"Exception ocurred while assigning role ({role.Name}) to member ({member.Username}): "
  1786. + ex.Message, MessageLevel.Error);
  1787. }
  1788. }
  1789.  
  1790. /// <summary>
  1791. /// Assigns the specified roles to a member, given you have the permission.
  1792. /// </summary>
  1793. /// <param name="guild">The guild you and the user are in.</param>
  1794. /// <param name="roles">The roles you wish to assign them.</param>
  1795. /// <param name="member">The member you wish to assign the role to.</param>
  1796. public void AssignRoleToMember(DiscordServer guild, List<DiscordRole> roles, DiscordMember member)
  1797. {
  1798. string url = Endpoints.BaseAPI + Endpoints.Guilds + $"/{guild.ID}" + Endpoints.Members + $"/{member.ID}";
  1799. List<string> rolesAsIds = new List<string>();
  1800. roles.ForEach(x => rolesAsIds.Add(x.ID));
  1801. string message = JsonConvert.SerializeObject(new { roles = rolesAsIds.ToArray() });
  1802. try
  1803. {
  1804. WebWrapper.Patch(url, token, message);
  1805. }
  1806. catch (Exception ex)
  1807. {
  1808. DebugLogger.Log($"Exception ocurred while assigning {roles.Count} role(s) to member ({member.Username}): "
  1809. + ex.Message, MessageLevel.Error);
  1810. }
  1811. }
  1812.  
  1813. /// <summary>
  1814. /// Creates and invite to the given channel.
  1815. /// </summary>
  1816. /// <param name="channel"></param>
  1817. /// <returns>The invite's id.</returns>
  1818. public string CreateInvite(DiscordChannel channel)
  1819. {
  1820. string url = Endpoints.BaseAPI + Endpoints.Channels + $"/{channel.ID}" + Endpoints.Invites;
  1821. try
  1822. {
  1823. var resopnse = JObject.Parse(WebWrapper.Post(url, token, "{\"validate\":\"\"}"));
  1824. if (resopnse != null)
  1825. {
  1826. return resopnse["code"].ToString();
  1827. }
  1828. }
  1829. catch (Exception ex)
  1830. {
  1831. DebugLogger.Log($"Error ocurred while creating invite for channel {channel.Name}: {ex.Message}", MessageLevel.Error);
  1832. }
  1833. return null;
  1834. }
  1835.  
  1836. /// <summary>
  1837. /// Deletes an invite by id
  1838. /// </summary>
  1839. /// <param name="id">The ID of the invite you wish to delete.</param>
  1840. public void DeleteInvite(string id)
  1841. {
  1842. string url = Endpoints.BaseAPI + Endpoints.Invites + $"/{id}";
  1843. try
  1844. {
  1845. WebWrapper.Delete(url, token);
  1846. }
  1847. catch (Exception ex)
  1848. {
  1849. DebugLogger.Log($"Error ocurred while deleting invite: {ex.Message}", MessageLevel.Error);
  1850. }
  1851. }
  1852.  
  1853. /// <summary>
  1854. /// Just prepends https://discord.gg/ to a given invite :)
  1855. /// </summary>
  1856. /// <param name="id"></param>
  1857. /// <returns>A full invite URL.</returns>
  1858. public string MakeInviteURLFromCode(string id) => "https://discord.gg/" + id;
  1859.  
  1860.  
  1861. /// <summary>
  1862. /// Runs the websocket connection for the client hooking up the appropriate events.
  1863. /// </summary>
  1864. /// <param name="useDotNetWebsocket">If true, DiscordSharp will connect using the .Net Framework's built-in WebSocketClasses.
  1865. /// Please do not use this on Mono or versions of Windows below 8/8.1</param>
  1866. public void Connect(bool useDotNetWebsocket = false)
  1867. {
  1868. CurrentGatewayURL = GetGatewayUrl();
  1869. if (string.IsNullOrEmpty(CurrentGatewayURL))
  1870. {
  1871. DebugLogger.Log("Gateway URL was null or empty?!", MessageLevel.Critical);
  1872. return;
  1873. }
  1874. DebugLogger.Log("Gateway retrieved: " + CurrentGatewayURL);
  1875.  
  1876. if (useDotNetWebsocket)
  1877. {
  1878. ws = new NetWebSocket(CurrentGatewayURL);
  1879. DebugLogger.Log("Using the built-in .Net websocket..");
  1880. }
  1881. else
  1882. {
  1883. ws = new WebSocketSharpSocket(CurrentGatewayURL);
  1884. DebugLogger.Log("Using WebSocketSharp websocket..");
  1885. }
  1886.  
  1887. ws.MessageReceived += (sender, e) =>
  1888. {
  1889. var message = new JObject();
  1890. try
  1891. {
  1892. message = JObject.Parse(e.Message);
  1893. }
  1894. catch(Exception ex)
  1895. {
  1896. DebugLogger.Log($"MessageReceived Error: {ex.Message}\n\n```{e.Message}\n```\n", MessageLevel.Error);
  1897. }
  1898.  
  1899. if (EnableVerboseLogging)
  1900. if (message["t"].ToString() != "READY")
  1901. DebugLogger.Log(message.ToString(), MessageLevel.Unecessary);
  1902.  
  1903. if (!message["t"].IsNullOrEmpty()) //contains a t parameter used for client events.
  1904. ClientPacketReceived(message);
  1905. else
  1906. MiscellaneousOpcodes(message);
  1907.  
  1908. if (!message["s"].IsNullOrEmpty())
  1909. Sequence = message["s"].ToObject<int>();
  1910.  
  1911. };
  1912. ws.SocketOpened += (sender, e) =>
  1913. {
  1914. SendIdentifyPacket();
  1915. SocketOpened?.Invoke(this, null);
  1916. };
  1917. ws.SocketClosed += (sender, e) =>
  1918. {
  1919. DiscordSocketClosedEventArgs scev = new DiscordSocketClosedEventArgs();
  1920. scev.Code = e.Code;
  1921. scev.Reason = e.Reason;
  1922. scev.WasClean = e.WasClean;
  1923. SocketClosed?.Invoke(this, scev);
  1924.  
  1925. if (Autoconnect && !e.WasClean)
  1926. {
  1927. PerformReconnection();
  1928. }
  1929. };
  1930. ws.Connect();
  1931. DebugLogger.Log("Connecting..");
  1932. }
  1933.  
  1934. private void MiscellaneousOpcodes(JObject message)
  1935. {
  1936. switch (message["d"].ToObject<int>())
  1937. {
  1938. case Opcodes.INVALIDATE_SESSION:
  1939. // TODO: the session was invalidated and a full reconnection must be performed.
  1940. DebugLogger.Log($"The session was invalidated. ", MessageLevel.Critical);
  1941. break;
  1942. }
  1943. }
  1944.  
  1945. private void PerformReconnection()
  1946. {
  1947. string resumeJson = JsonConvert.SerializeObject(new
  1948. {
  1949. op = Opcodes.RESUME,
  1950. d = new
  1951. {
  1952. seq = Sequence,
  1953. token = DiscordClient.token,
  1954. session_id = SessionID
  1955. }
  1956. });
  1957. }
  1958.  
  1959. private void ClientPacketReceived(JObject message)
  1960. {
  1961. switch (message["t"].ToString())
  1962. {
  1963. case ("READY"):
  1964. Sequence = message["s"].ToObject<int>();
  1965. DiscordGatewayVersion = message["d"]["v"].ToObject<int>();
  1966. HeartbeatInterval = message["d"]["heartbeat_interval"].ToObject<int>();
  1967. BeginHeartbeatTask();
  1968. if (WriteLatestReady)
  1969. using (var sw = new StreamWriter("READY_LATEST.txt"))
  1970. sw.Write(message);
  1971. Me = JsonConvert.DeserializeObject<DiscordMember>(message["d"]["user"].ToString());
  1972. Me.parentclient = this;
  1973. IsBotAccount = message["d"]["user"]["bot"].IsNullOrEmpty() ? false : message["d"]["user"]["bot"].ToObject<bool>();
  1974. ClientPrivateInformation.Avatar = Me.Avatar;
  1975. ClientPrivateInformation.Username = Me.Username;
  1976. GetChannelsList(message);
  1977. SessionID = message["d"]["session_id"].ToString();
  1978.  
  1979. //TESTING
  1980. string[] guildID = new string[ServersList.Count];
  1981. for (int i = 0; i < guildID.Length; i++)
  1982. guildID[i] = ServersList[i].ID;
  1983.  
  1984. if (RequestAllUsersOnStartup)
  1985. {
  1986. string wsChunkTest = JsonConvert.SerializeObject(new
  1987. {
  1988. op = 8,
  1989. d = new
  1990. {
  1991. guild_id = guildID,
  1992. query = "",
  1993. limit = 0
  1994. }
  1995. });
  1996. ws.Send(wsChunkTest);
  1997. }
  1998.  
  1999. ReadyComplete = true;
  2000.  
  2001. Task.Run(() =>
  2002. {
  2003. Task.Delay(3000);
  2004. Connected?.Invoke(this, new DiscordConnectEventArgs { User = Me });
  2005. }); //fire and forget waiting of up to 3 seconds for guilds to become available.
  2006. break;
  2007. case ("GUILD_MEMBERS_CHUNK"):
  2008. GuildMemberChunkEvents(message);
  2009. break;
  2010. case ("GUILD_MEMBER_REMOVE"):
  2011. GuildMemberRemoveEvents(message);
  2012. break;
  2013. case ("GUILD_MEMBER_ADD"):
  2014. GuildMemberAddEvents(message);
  2015. break;
  2016. case ("GUILD_DELETE"):
  2017. GuildDeleteEvents(message);
  2018. break;
  2019. case ("GUILD_CREATE"):
  2020. GuildCreateEvents(message);
  2021. break;
  2022. case ("GUILD_MEMBER_UPDATE"):
  2023. GuildMemberUpdateEvents(message);
  2024. break;
  2025. case ("GUILD_UPDATE"):
  2026. GuildUpdateEvents(message);
  2027. break;
  2028. case ("GUILD_ROLE_DELETE"):
  2029. GuildRoleDeleteEvents(message);
  2030. break;
  2031. case ("GUILD_ROLE_UPDATE"):
  2032. GuildRoleUpdateEvents(message);
  2033. break;
  2034. case ("PRESENCE_UPDATE"):
  2035. PresenceUpdateEvents(message);
  2036. break;
  2037. case ("MESSAGE_UPDATE"):
  2038. MessageUpdateEvents(message);
  2039. break;
  2040. case ("TYPING_START"):
  2041. DiscordServer server = ServersList.Find(x => x.Channels.Find(y => y.ID == message["d"]["channel_id"].ToString()) != null);
  2042. if (server != null)
  2043. {
  2044. DiscordChannel channel = server.Channels.Find(x => x.ID == message["d"]["channel_id"].ToString());
  2045. DiscordMember uuser = server.GetMemberByKey(message["d"]["user_id"].ToString());
  2046. if (UserTypingStart != null)
  2047. UserTypingStart(this, new DiscordTypingStartEventArgs { user = uuser, Channel = channel, Timestamp = int.Parse(message["d"]["timestamp"].ToString()) });
  2048. }
  2049. break;
  2050. case ("MESSAGE_CREATE"):
  2051. MessageCreateEvents(message);
  2052. break;
  2053. case ("CHANNEL_CREATE"):
  2054. ChannelCreateEvents(message);
  2055. break;
  2056. case ("VOICE_STATE_UPDATE"):
  2057. VoiceStateUpdateEvents(message);
  2058. break;
  2059. case ("VOICE_SERVER_UPDATE"):
  2060. VoiceServerUpdateEvents(message);
  2061. break;
  2062. case ("MESSAGE_DELETE"):
  2063. MessageDeletedEvents(message);
  2064. break;
  2065. case ("USER_UPDATE"):
  2066. UserUpdateEvents(message);
  2067. break;
  2068. case ("CHANNEL_UPDATE"):
  2069. ChannelUpdateEvents(message);
  2070. break;
  2071. case ("CHANNEL_DELETE"):
  2072. ChannelDeleteEvents(message);
  2073. break;
  2074. case ("GUILD_BAN_ADD"):
  2075. GuildMemberBannedEvents(message);
  2076. break;
  2077. case ("GUILD_BAN_REMOVE"):
  2078. GuildMemberBanRemovedEvents(message);
  2079. break;
  2080. case ("MESSAGE_ACK"): //ignore this message, it's irrelevant
  2081. break;
  2082. default:
  2083. if (UnknownMessageTypeReceived != null)
  2084. UnknownMessageTypeReceived(this, new UnknownMessageEventArgs { RawJson = message });
  2085. break;
  2086. }
  2087. }
  2088.  
  2089. private void SendIdentifyPacket()
  2090. {
  2091. string initJson = JsonConvert.SerializeObject(new
  2092. {
  2093. op = 2,
  2094. d = new
  2095. {
  2096. v = 4,
  2097. token = token,
  2098. /*large_threshold = 50,*/
  2099. properties = DiscordProperties
  2100. }
  2101. });
  2102.  
  2103. DebugLogger.Log("Sending initJson ( " + initJson + " )");
  2104.  
  2105. ws.Send(initJson);
  2106. }
  2107.  
  2108. private void BeginHeartbeatTask()
  2109. {
  2110. KeepAliveTaskTokenSource = new CancellationTokenSource();
  2111. KeepAliveTaskToken = KeepAliveTaskTokenSource.Token;
  2112. KeepAliveTask = new Task(() =>
  2113. {
  2114. while (true)
  2115. {
  2116. DebugLogger.Log("Hello from inside KeepAliveTask!");
  2117. Thread.Sleep(HeartbeatInterval);
  2118. KeepAlive();
  2119. }
  2120. }, KeepAliveTaskToken);
  2121. KeepAliveTask.Start();
  2122. DebugLogger.Log("Began keepalive task..");
  2123. }
  2124.  
  2125. private void GuildMemberChunkEvents(JObject message)
  2126. {
  2127. if (!message["d"]["members"].IsNullOrEmpty())
  2128. {
  2129. DiscordServer inServer = ServersList.Find(x => x.ID == message["d"]["guild_id"].ToString());
  2130. JArray membersAsArray = JArray.Parse(message["d"]["members"].ToString());
  2131. foreach (var member in membersAsArray)
  2132. {
  2133. //if (GuildHasMemberWithID(inServer, member["user"]["id"].ToString()))
  2134. // continue;
  2135. DiscordMember _member = JsonConvert.DeserializeObject<DiscordMember>(member["user"].ToString());
  2136. if (!member["user"]["roles"].IsNullOrEmpty())
  2137. {
  2138. JArray rollsArray = JArray.Parse(member["user"]["roles"].ToString());
  2139. if (rollsArray.Count > 0)
  2140. {
  2141. foreach (var rollID in rollsArray)
  2142. _member.Roles.Add(inServer.Roles.Find(x => x.ID == rollID.ToString()));
  2143. }
  2144. }
  2145. _member.Muted = member["mute"].ToObject<bool>();
  2146. _member.Deaf = member["deaf"].ToObject<bool>();
  2147. _member.Roles.Add(inServer.Roles.Find(x => x.Name == "@everyone"));
  2148. _member.Status = Status.Offline;
  2149. _member.parentclient = this;
  2150. _member.Parent = inServer;
  2151. inServer.AddMember(_member);
  2152.  
  2153. ///Check private channels
  2154. DiscordPrivateChannel _channel = PrivateChannels.Find(x => x.user_id == _member.ID);
  2155. if (_channel != null)
  2156. {
  2157. DebugLogger.Log("Found user for private channel!", MessageLevel.Debug);
  2158. _channel.Recipient = _member;
  2159. }
  2160. }
  2161. }
  2162. }
  2163.  
  2164. private void GuildMemberBanRemovedEvents(JObject message)
  2165. {
  2166. DiscordBanRemovedEventArgs e = new DiscordBanRemovedEventArgs();
  2167.  
  2168. e.Guild = ServersList.Find(x => x.ID == message["d"]["guild_id"].ToString());
  2169. e.MemberStub = JsonConvert.DeserializeObject<DiscordMember>(message["d"]["user"].ToString());
  2170.  
  2171. if (BanRemoved != null)
  2172. BanRemoved(this, e);
  2173. }
  2174.  
  2175. private void GuildMemberBannedEvents(JObject message)
  2176. {
  2177. DiscordGuildBanEventArgs e = new DiscordGuildBanEventArgs();
  2178. e.Server = ServersList.Find(x => x.ID == message["d"]["guild_id"].ToString());
  2179. if (e.Server != null)
  2180. {
  2181. e.MemberBanned = e.Server.GetMemberByKey(message["d"]["user"]["id"].ToString());
  2182. if (e.MemberBanned != null)
  2183. {
  2184. if (GuildMemberBanned != null)
  2185. GuildMemberBanned(this, e);
  2186. ServersList.Find(x => x.ID == e.Server.ID).RemoveMember(e.MemberBanned.ID);
  2187. }
  2188. else
  2189. {
  2190. DebugLogger.Log("Error in GuildMemberBannedEvents: MemberBanned is null, attempting internal index of removed members.", MessageLevel.Error);
  2191. e.MemberBanned = RemovedMembers.Find(x => x.ID == message["d"]["user"]["id"].ToString());
  2192. if (e.MemberBanned != null)
  2193. {
  2194. if (GuildMemberBanned != null)
  2195. GuildMemberBanned(this, e);
  2196. }
  2197. else
  2198. {
  2199. DebugLogger.Log("Error in GuildMemberBannedEvents: MemberBanned is null, not even found in internal index!", MessageLevel.Error);
  2200. }
  2201. }
  2202. }
  2203. else
  2204. {
  2205. DebugLogger.Log("Error in GuildMemberBannedEvents: Server is null?!", MessageLevel.Error);
  2206. }
  2207. }
  2208.  
  2209. private void VoiceServerUpdateEvents(JObject message)
  2210. {
  2211. VoiceClient.VoiceEndpoint = message["d"]["endpoint"].ToString();
  2212. VoiceClient.Token = message["d"]["token"].ToString();
  2213.  
  2214. VoiceClient.Guild = ServersList.Find(x => x.ID == message["d"]["guild_id"].ToString());
  2215. VoiceClient.Me = Me;
  2216.  
  2217. VoiceClient.PacketReceived += (sender, e) =>
  2218. {
  2219. if (AudioPacketReceived != null)
  2220. AudioPacketReceived(sender, e);
  2221. };
  2222.  
  2223. VoiceClient.DebugMessageReceived += (sender, e) =>
  2224. {
  2225. if (VoiceClientDebugMessageReceived != null)
  2226. VoiceClientDebugMessageReceived(this, e);
  2227. };
  2228.  
  2229. ConnectToVoiceAsync();
  2230. }
  2231.  
  2232. #if NETFX4_5
  2233. private void ConnectToVoiceAsync()
  2234. {
  2235. VoiceClient.InitializeOpusEncoder();
  2236. VoiceThread = new Thread(() => VoiceClient.Initiate());
  2237. VoiceThread.Start();
  2238. }
  2239. #else
  2240. private Task ConnectToVoiceAsync()
  2241. {
  2242. VoiceClient.InitializeOpusEncoder();
  2243. return Task.Factory.StartNew(() => VoiceClient.Initiate());
  2244. }
  2245. #endif
  2246.  
  2247. /// <summary>
  2248. /// Kicks a specified DiscordMember from the guild that's assumed from their
  2249. /// parent property.
  2250. /// </summary>
  2251. /// <param name="member"></param>
  2252. public void KickMember(DiscordMember member)
  2253. {
  2254. string url = Endpoints.BaseAPI + Endpoints.Guilds + $"/{member.Parent.ID}" + Endpoints.Members + $"/{member.ID}";
  2255. try
  2256. {
  2257. WebWrapper.Delete(url, token);
  2258. }
  2259. catch (Exception ex)
  2260. {
  2261. DebugLogger.Log($"Error during KickMember\n\t{ex.Message}\n\t{ex.StackTrace}", MessageLevel.Error);
  2262. }
  2263. }
  2264.  
  2265. /// <summary>
  2266. /// Bans a specified DiscordMember from the guild that's assumed from their
  2267. /// parent property.
  2268. /// </summary>
  2269. /// <param name="member"></param>
  2270. /// <param name="days">The number of days the user should be banned for, or 0 for infinite.</param>
  2271. public DiscordMember BanMember(DiscordMember member, int days = 0)
  2272. {
  2273. string url = Endpoints.BaseAPI + Endpoints.Guilds + $"/{member.Parent.ID}" + Endpoints.Bans + $"/{member.ID}";
  2274. url += $"?delete-message-days={days}";
  2275. try
  2276. {
  2277. WebWrapper.Put(url, token);
  2278. return member;
  2279. }
  2280. catch (Exception ex)
  2281. {
  2282. DebugLogger.Log($"Error during BanMember\n\t{ex.Message}\n\t{ex.StackTrace}", MessageLevel.Error);
  2283. return null;
  2284. }
  2285. }
  2286.  
  2287. /// <summary>
  2288. /// Bans a specified DiscordMember from the guild that's assumed from their
  2289. /// parent property.
  2290. /// </summary>
  2291. /// <param name="member"></param>
  2292. /// <param name="serverOverride"></param>
  2293. /// <param name="days"></param>
  2294. /// <returns></returns>
  2295. public DiscordMember BanMember(DiscordMember member, DiscordServer serverOverride, int days = 0)
  2296. {
  2297. string url = Endpoints.BaseAPI + Endpoints.Guilds + $"/{serverOverride.ID}" + Endpoints.Bans + $"/{member.ID}";
  2298. url += $"?delete-message-days={days}";
  2299. try
  2300. {
  2301. WebWrapper.Put(url, token);
  2302. return member;
  2303. }
  2304. catch (Exception ex)
  2305. {
  2306. DebugLogger.Log($"Error during BanMember\n\t{ex.Message}\n\t{ex.StackTrace}", MessageLevel.Error);
  2307. return null;
  2308. }
  2309. }
  2310.  
  2311. /// <summary>
  2312. /// Retrieves a DiscordMember List of members banned in the specified server.
  2313. /// </summary>
  2314. /// <param name="server"></param>
  2315. /// <returns>Null if no permission.</returns>
  2316. public List<DiscordMember> GetBans(DiscordServer server)
  2317. {
  2318. List<DiscordMember> returnVal = new List<DiscordMember>();
  2319. string url = Endpoints.BaseAPI + Endpoints.Guilds + $"/{server.ID}" + Endpoints.Bans;
  2320. try
  2321. {
  2322. var __res = WebWrapper.Get(url, token);
  2323. var permissionCheck = JObject.Parse(__res);
  2324. {
  2325. if (!permissionCheck["message"].IsNullOrEmpty())
  2326. return null; //no permission
  2327. }
  2328. JArray response = JArray.Parse(__res);
  2329. if (response != null && response.Count > 0)
  2330. {
  2331. DebugLogger.Log($"Ban count: {response.Count}");
  2332.  
  2333. foreach (var memberStub in response)
  2334. {
  2335. DiscordMember temp = JsonConvert.DeserializeObject<DiscordMember>(memberStub["user"].ToString());
  2336. if (temp != null)
  2337. returnVal.Add(temp);
  2338. else
  2339. DebugLogger.Log($"memberStub[\"user\"] was null?! Username: {memberStub["user"]["username"].ToString()} ID: {memberStub["user"]["username"].ToString()}", MessageLevel.Error);
  2340. }
  2341. }
  2342. else
  2343. return returnVal;
  2344. }
  2345. catch (Exception ex)
  2346. {
  2347. DebugLogger.Log($"An error ocurred while retrieving bans for server \"{server.Name}\"\n\tMessage: {ex.Message}\n\tStack: {ex.StackTrace}",
  2348. MessageLevel.Error);
  2349. }
  2350. return returnVal;
  2351. }
  2352.  
  2353. /// <summary>
  2354. /// Removes a ban on the user.
  2355. /// </summary>
  2356. /// <param name="guild">The guild to lift the ban from.</param>
  2357. /// <param name="userID">The ID of the user to lift the ban.</param>
  2358. public void RemoveBan(DiscordServer guild, string userID)
  2359. {
  2360. string url = Endpoints.BaseAPI + Endpoints.Guilds + $"/{guild.ID}" + Endpoints.Bans + $"/{userID}";
  2361. try
  2362. {
  2363. WebWrapper.Delete(url, token);
  2364. }
  2365. catch (Exception ex)
  2366. {
  2367. DebugLogger.Log($"Error during RemoveBan\n\tMessage: {ex.Message}\n\tStack: {ex.StackTrace}", MessageLevel.Error);
  2368. }
  2369. }
  2370.  
  2371. /// <summary>
  2372. /// Removes a ban on the user.
  2373. /// </summary>
  2374. /// <param name="guild">The guild to lift the ban from.</param>
  2375. /// <param name="member">The DiscordMember object of the user to lift the ban from, assuming you have it.</param>
  2376. public void RemoveBan(DiscordServer guild, DiscordMember member)
  2377. {
  2378. string url = Endpoints.BaseAPI + Endpoints.Guilds + $"/{guild.ID}" + Endpoints.Bans + $"/{member.ID}";
  2379. try
  2380. {
  2381. WebWrapper.Delete(url, token);
  2382. }
  2383. catch (Exception ex)
  2384. {
  2385. DebugLogger.Log($"Error during RemoveBan\n\tMessage: {ex.Message}\n\tStack: {ex.StackTrace}", MessageLevel.Error);
  2386. }
  2387. }
  2388.  
  2389. /// <summary>
  2390. /// Echoes a received audio packet back.
  2391. /// </summary>
  2392. /// <param name="packet"></param>
  2393. public void EchoPacket(DiscordAudioPacket packet)
  2394. {
  2395. if (VoiceClient != null && ConnectedToVoice())
  2396. VoiceClient.EchoPacket(packet).Wait();
  2397. }
  2398.  
  2399. /// <summary>
  2400. /// Connects to a given voice channel.
  2401. /// </summary>
  2402. /// <param name="channel">The channel to connect to. </param>
  2403. /// <param name="voiceConfig">The voice configuration to use. If null, default values will be used.</param>
  2404. /// <param name="clientMuted">Whether or not the client will connect muted. Defaults to false.</param>
  2405. /// <param name="clientDeaf">Whether or not the client will connect deaf. Defaults to false.</param>
  2406. public void ConnectToVoiceChannel(DiscordChannel channel, DiscordVoiceConfig voiceConfig = null, bool clientMuted = false, bool clientDeaf = false)
  2407. {
  2408. if (channel.Type != ChannelType.Voice)
  2409. throw new InvalidOperationException($"Channel '{channel.ID}' is not a voice channel!");
  2410.  
  2411. if (ConnectedToVoice())
  2412. DisconnectFromVoice();
  2413.  
  2414. if (VoiceClient == null)
  2415. {
  2416. if (voiceConfig == null)
  2417. {
  2418. VoiceClient = new DiscordVoiceClient(this, new DiscordVoiceConfig());
  2419. }
  2420. else
  2421. VoiceClient = new DiscordVoiceClient(this, voiceConfig);
  2422. }
  2423. VoiceClient.Channel = channel;
  2424. VoiceClient.ErrorReceived += (sender, e) =>
  2425. {
  2426. if (GetLastVoiceClientLogger != null)
  2427. {
  2428. GetLastVoiceClientLogger = VoiceClient.GetDebugLogger;
  2429. DisconnectFromVoice();
  2430. }
  2431. };
  2432. VoiceClient.UserSpeaking += (sender, e) =>
  2433. {
  2434. if (UserSpeaking != null)
  2435. UserSpeaking(this, e);
  2436. };
  2437. VoiceClient.VoiceConnectionComplete += (sender, e) =>
  2438. {
  2439. if (VoiceClientConnected != null)
  2440. VoiceClientConnected(this, e);
  2441. };
  2442. VoiceClient.QueueEmpty += (sender, e) =>
  2443. {
  2444. VoiceQueueEmpty?.Invoke(this, e);
  2445. };
  2446.  
  2447. string joinVoicePayload = JsonConvert.SerializeObject(new
  2448. {
  2449. op = 4,
  2450. d = new
  2451. {
  2452. guild_id = channel.Parent.ID,
  2453. channel_id = channel.ID,
  2454. self_mute = clientMuted,
  2455. self_deaf = clientDeaf
  2456. }
  2457. });
  2458.  
  2459. ws.Send(joinVoicePayload);
  2460. }
  2461.  
  2462. /// <summary>
  2463. /// Clears the internal message log cache
  2464. /// </summary>
  2465. /// <returns>The number of internal messages cleared.</returns>
  2466. public int ClearInternalMessageLog()
  2467. {
  2468. int totalCount = MessageLog.Count;
  2469. MessageLog.Clear();
  2470. return totalCount;
  2471. }
  2472.  
  2473. /// <summary>
  2474. /// Iterates through a server's members and removes offline users.
  2475. /// </summary>
  2476. /// <param name="server"></param>
  2477. /// <returns>The amount of users cleared.</returns>
  2478. public int ClearOfflineUsersFromServer(DiscordServer server)
  2479. {
  2480. return server.ClearOfflineMembers();
  2481. }
  2482.  
  2483. /// <summary>
  2484. /// Also disposes
  2485. /// </summary>
  2486. public void DisconnectFromVoice()
  2487. {
  2488. string disconnectMessage = JsonConvert.SerializeObject(new
  2489. {
  2490. op = 4,
  2491. d = new
  2492. {
  2493. guild_id = VoiceClient != null && VoiceClient.Channel != null ? VoiceClient.Channel.Parent.ID : (object)null,
  2494. channel_id = (object)null,
  2495. self_mute = true,
  2496. self_deaf = false
  2497. }
  2498. });
  2499. if (VoiceClient != null)
  2500. {
  2501. try
  2502. {
  2503. VoiceClient.Dispose();
  2504. VoiceClient = null;
  2505.  
  2506.  
  2507. ws.Send(disconnectMessage);
  2508. }
  2509. catch
  2510. { }
  2511. }
  2512. if (ws != null)
  2513. ws.Send(disconnectMessage);
  2514. VoiceClient = null;
  2515. if (VoiceThread != null)
  2516. VoiceThread.Abort();
  2517. DebugLogger.Log($"Disconnected from voice. VoiceClient null: {VoiceClient == null}");
  2518. }
  2519.  
  2520. /// <summary>
  2521. ///
  2522. /// </summary>
  2523. /// <returns>The current VoiceClient or null.</returns>
  2524. public DiscordVoiceClient GetVoiceClient()
  2525. {
  2526. if (ConnectedToVoice() && VoiceClient != null)
  2527. return VoiceClient;
  2528.  
  2529. return null;
  2530. }
  2531.  
  2532. private void GuildMemberUpdateEvents(JObject message)
  2533. {
  2534. DiscordServer server = ServersList.Find(x => x.ID == message["d"]["guild_id"].ToString());
  2535.  
  2536. DiscordMember memberUpdated = server.GetMemberByKey(message["d"]["user"]["id"].ToString());
  2537. if (memberUpdated != null)
  2538. {
  2539. memberUpdated.Username = message["d"]["user"]["username"].ToString();
  2540. if(message["d"]["nick"] != null)
  2541. {
  2542. if (message["d"]["nick"].ToString() == null)
  2543. memberUpdated.Nickname = ""; //No nickname
  2544. else
  2545. memberUpdated.Nickname = message["d"]["nick"].ToString();
  2546. }
  2547.  
  2548. if (!message["d"]["user"]["avatar"].IsNullOrEmpty())
  2549. memberUpdated.Avatar = message["d"]["user"]["avatar"].ToString();
  2550. memberUpdated.Discriminator = message["d"]["user"]["discriminator"].ToString();
  2551. memberUpdated.ID = message["d"]["user"]["id"].ToString();
  2552.  
  2553. foreach (var roles in message["d"]["roles"])
  2554. {
  2555. memberUpdated.Roles.Add(server.Roles.Find(x => x.ID == roles.ToString()));
  2556. }
  2557.  
  2558. server.AddMember(memberUpdated);
  2559. GuildMemberUpdated?.Invoke(this, new DiscordGuildMemberUpdateEventArgs { MemberUpdate = memberUpdated, RawJson = message, ServerUpdated = server });
  2560. }
  2561. else
  2562. {
  2563. DebugLogger.Log("memberUpdated was null?!?!?!", MessageLevel.Debug);
  2564. }
  2565. }
  2566.  
  2567. private void GuildRoleUpdateEvents(JObject message)
  2568. {
  2569. DiscordServer inServer = ServersList.Find(x => x.ID == message["d"]["guild_id"].ToString());
  2570. DiscordRole roleUpdated = new DiscordRole
  2571. {
  2572. Name = message["d"]["role"]["name"].ToString(),
  2573. Position = message["d"]["role"]["position"].ToObject<int>(),
  2574. Permissions = new DiscordPermission(message["d"]["role"]["permissions"].ToObject<uint>()),
  2575. Managed = message["d"]["role"]["managed"].ToObject<bool>(),
  2576. Hoist = message["d"]["role"]["hoist"].ToObject<bool>(),
  2577. Color = new Color(message["d"]["role"]["color"].ToObject<int>().ToString("x")),
  2578. ID = message["d"]["role"]["id"].ToString(),
  2579. };
  2580.  
  2581. ServersList.Find(x => x.ID == inServer.ID).Roles.Remove(ServersList.Find(x => x.ID == inServer.ID).Roles.Find(y => y.ID == roleUpdated.ID));
  2582. ServersList.Find(x => x.ID == inServer.ID).Roles.Add(roleUpdated);
  2583.  
  2584. RoleUpdated?.Invoke(this, new DiscordGuildRoleUpdateEventArgs { RawJson = message, RoleUpdated = roleUpdated, InServer = inServer });
  2585. }
  2586.  
  2587. private void GuildRoleDeleteEvents(JObject message)
  2588. {
  2589. DiscordServer inServer = ServersList.Find(x => x.ID == message["d"]["guild_id"].ToString());
  2590. DiscordRole deletedRole = inServer.Roles.Find(x => x.ID == message["d"]["role_id"].ToString());
  2591.  
  2592. try
  2593. {
  2594. ServersList.Find(x => x.ID == inServer.ID).Roles.Remove(ServersList.Find(x => x.ID == inServer.ID).Roles.Find(y => y.ID == deletedRole.ID));
  2595. }
  2596. catch (Exception ex)
  2597. {
  2598. DebugLogger.Log($"Couldn't delete role with ID {message["d"]["role_id"].ToString()}! ({ex.Message})", MessageLevel.Critical);
  2599. }
  2600.  
  2601. RoleDeleted?.Invoke(this, new DiscordGuildRoleDeleteEventArgs { DeletedRole = deletedRole, Guild = inServer, RawJson = message });
  2602. }
  2603.  
  2604. /// <summary>
  2605. /// Creates a default role in the specified guild.
  2606. /// </summary>
  2607. /// <param name="guild">The guild to make the role in.</param>
  2608. /// <returns>The newly created role.</returns>
  2609. public DiscordRole CreateRole(DiscordServer guild)
  2610. {
  2611. string url = Endpoints.BaseAPI + Endpoints.Guilds + $"/{guild.ID}" + Endpoints.Roles;
  2612.  
  2613. try
  2614. {
  2615. var result = JObject.Parse(WebWrapper.Post(url, token, ""));
  2616.  
  2617. if (result != null)
  2618. {
  2619. DiscordRole d = new DiscordRole
  2620. {
  2621. Color = new Color(result["color"].ToObject<int>().ToString("x")),
  2622. Hoist = result["hoist"].ToObject<bool>(),
  2623. ID = result["id"].ToString(),
  2624. Managed = result["managed"].ToObject<bool>(),
  2625. Name = result["name"].ToString(),
  2626. Permissions = new DiscordPermission(result["permissions"].ToObject<uint>()),
  2627. Position = result["position"].ToObject<int>()
  2628. };
  2629.  
  2630. ServersList.Find(x => x.ID == guild.ID).Roles.Add(d);
  2631. return d;
  2632. }
  2633. }
  2634. catch (Exception ex)
  2635. {
  2636. DebugLogger.Log($"Error ocurred while creating role in guild {guild.Name}: {ex.Message}", MessageLevel.Error);
  2637. }
  2638. return null;
  2639. }
  2640.  
  2641. /// <summary>
  2642. /// Edits a role with the new role information.
  2643. /// </summary>
  2644. /// <param name="guild">The guild the role is in.</param>
  2645. /// <param name="newRole">the new role.</param>
  2646. /// <returns>The newly edited role returned from Discord.</returns>
  2647. public DiscordRole EditRole(DiscordServer guild, DiscordRole newRole)
  2648. {
  2649. string url = Endpoints.BaseAPI + Endpoints.Guilds + $"/{guild.ID}" + Endpoints.Roles + $"/{newRole.ID}";
  2650. string request = JsonConvert.SerializeObject(
  2651. new
  2652. {
  2653. color = decimal.Parse(newRole.Color.ToDecimal().ToString()),
  2654. hoist = newRole.Hoist,
  2655. name = newRole.Name,
  2656. permissions = newRole.Permissions.GetRawPermissions()
  2657. }
  2658. );
  2659.  
  2660. try
  2661. {
  2662. var result = JObject.Parse(WebWrapper.Patch(url, token, request));
  2663. if (result != null)
  2664. {
  2665. DiscordRole d = new DiscordRole
  2666. {
  2667. Color = new Color(result["color"].ToObject<int>().ToString("x")),
  2668. Hoist = result["hoist"].ToObject<bool>(),
  2669. ID = result["id"].ToString(),
  2670. Managed = result["managed"].ToObject<bool>(),
  2671. Name = result["name"].ToString(),
  2672. Permissions = new DiscordPermission(result["permissions"].ToObject<uint>()),
  2673. Position = result["position"].ToObject<int>()
  2674. };
  2675.  
  2676. ServersList.Find(x => x.ID == guild.ID).Roles.Remove(d);
  2677. ServersList.Find(x => x.ID == guild.ID).Roles.Add(d);
  2678. return d;
  2679. }
  2680. }
  2681. catch (Exception ex)
  2682. {
  2683. DebugLogger.Log($"Error ocurred while editing role ({newRole.Name}): {ex.Message}", MessageLevel.Error);
  2684. }
  2685.  
  2686. return null;
  2687. }
  2688.  
  2689. /// <summary>
  2690. /// Deletes a specified role.
  2691. /// </summary>
  2692. /// <param name="guild">The guild the role is in.</param>
  2693. /// <param name="role">The role to delete.</param>
  2694. public void DeleteRole(DiscordServer guild, DiscordRole role)
  2695. {
  2696. string url = Endpoints.BaseAPI + Endpoints.Guilds + $"/{guild.ID}" + Endpoints.Roles + $"/{role.ID}";
  2697. try
  2698. {
  2699. WebWrapper.Delete(url, token);
  2700. }
  2701. catch (Exception ex)
  2702. {
  2703. DebugLogger.Log($"Error ocurred while deleting role ({role.Name}): {ex.Message}", MessageLevel.Error);
  2704. }
  2705. }
  2706.  
  2707. private void GuildUpdateEvents(JObject message)
  2708. {
  2709. DiscordServer oldServer = ServersList.Find(x => x.ID == message["d"]["id"].ToString());
  2710. DiscordServer newServer = oldServer.ShallowCopy();
  2711.  
  2712. newServer.Name = message["d"]["name"].ToString();
  2713. newServer.ID = message["d"]["id"].ToString();
  2714. newServer.parentclient = this;
  2715. newServer.Roles = new List<DiscordRole>();
  2716. newServer.Region = message["d"]["region"].ToString();
  2717. if (!message["d"]["icon"].IsNullOrEmpty())
  2718. {
  2719. newServer.icon = message["d"]["icon"].ToString();
  2720. }
  2721. if (!message["d"]["roles"].IsNullOrEmpty())
  2722. {
  2723. foreach (var roll in message["d"]["roles"])
  2724. {
  2725. DiscordRole t = new DiscordRole
  2726. {
  2727. Color = new DiscordSharp.Color(roll["color"].ToObject<int>().ToString("x")),
  2728. Name = roll["name"].ToString(),
  2729. Permissions = new DiscordPermission(roll["permissions"].ToObject<uint>()),
  2730. Position = roll["position"].ToObject<int>(),
  2731. Managed = roll["managed"].ToObject<bool>(),
  2732. ID = roll["id"].ToString(),
  2733. Hoist = roll["hoist"].ToObject<bool>()
  2734. };
  2735. newServer.Roles.Add(t);
  2736. }
  2737. }
  2738. else
  2739. {
  2740. newServer.Roles = oldServer.Roles;
  2741. }
  2742. newServer.Channels = new List<DiscordChannel>();
  2743. if (!message["d"]["channels"].IsNullOrEmpty())
  2744. {
  2745. foreach (var u in message["d"]["channels"])
  2746. {
  2747. DiscordChannel tempSub = new DiscordChannel();
  2748. tempSub.Client = this;
  2749. tempSub.ID = u["id"].ToString();
  2750. tempSub.Name = u["name"].ToString();
  2751. tempSub.Type = u["type"].ToObject<ChannelType>();
  2752.  
  2753. if (!u["topic"].IsNullOrEmpty())
  2754. tempSub.Topic = u["topic"].ToString();
  2755. if (tempSub.Type == ChannelType.Voice && !u["bitrate"].IsNullOrEmpty())
  2756. tempSub.Bitrate = u["bitrate"].ToObject<int>();
  2757.  
  2758. tempSub.Parent = newServer;
  2759. List<DiscordPermissionOverride> permissionoverrides = new List<DiscordPermissionOverride>();
  2760. foreach (var o in u["permission_overwrites"])
  2761. {
  2762. DiscordPermissionOverride dpo = new DiscordPermissionOverride(o["allow"].ToObject<uint>(), o["deny"].ToObject<uint>());
  2763. dpo.type = o["type"].ToObject<DiscordPermissionOverride.OverrideType>();
  2764. dpo.id = o["id"].ToString();
  2765.  
  2766. permissionoverrides.Add(dpo);
  2767. }
  2768. tempSub.PermissionOverrides = permissionoverrides;
  2769.  
  2770. newServer.Channels.Add(tempSub);
  2771. }
  2772. }
  2773. else
  2774. {
  2775. newServer.Channels = oldServer.Channels;
  2776. }
  2777. if (!message["d"]["members"].IsNullOrEmpty())
  2778. {
  2779. foreach (var mm in message["d"]["members"])
  2780. {
  2781. DiscordMember member = JsonConvert.DeserializeObject<DiscordMember>(mm["user"].ToString());
  2782. member.parentclient = this;
  2783. member.Parent = newServer;
  2784.  
  2785. JArray rawRoles = JArray.Parse(mm["roles"].ToString());
  2786. if (rawRoles.Count > 0)
  2787. {
  2788. foreach (var role in rawRoles.Children())
  2789. {
  2790. member.Roles.Add(newServer.Roles.Find(x => x.ID == role.Value<string>()));
  2791. }
  2792. }
  2793. else
  2794. {
  2795. member.Roles.Add(newServer.Roles.Find(x => x.Name == "@everyone"));
  2796. }
  2797.  
  2798. newServer.AddMember(member);
  2799. }
  2800. }
  2801. else
  2802. {
  2803. newServer.Members = oldServer.Members;
  2804. }
  2805. if (!message["d"]["owner_id"].IsNullOrEmpty())
  2806. {
  2807. newServer.Owner = newServer.GetMemberByKey(message["d"]["owner_id"].ToString());
  2808. DebugLogger.Log($"Transferred ownership from user '{oldServer.Owner.Username}' to {newServer.Owner.Username}.");
  2809. }
  2810. ServersList.Remove(oldServer);
  2811. ServersList.Add(newServer);
  2812. DiscordServerUpdateEventArgs dsuea = new DiscordServerUpdateEventArgs { NewServer = newServer, OldServer = oldServer };
  2813. GuildUpdated?.Invoke(this, dsuea);
  2814. }
  2815.  
  2816. private void ChannelDeleteEvents(JObject message)
  2817. {
  2818. if (!message["d"]["recipient"].IsNullOrEmpty())
  2819. {
  2820. //private channel removed
  2821. DiscordPrivateChannelDeleteEventArgs e = new DiscordPrivateChannelDeleteEventArgs();
  2822. e.PrivateChannelDeleted = PrivateChannels.Find(x => x.ID == message["d"]["id"].ToString());
  2823. if (e.PrivateChannelDeleted != null)
  2824. {
  2825. if (PrivateChannelDeleted != null)
  2826. PrivateChannelDeleted(this, e);
  2827. PrivateChannels.Remove(e.PrivateChannelDeleted);
  2828. }
  2829. else
  2830. {
  2831. DebugLogger.Log("Error in ChannelDeleteEvents: PrivateChannel is null!", MessageLevel.Error);
  2832. }
  2833. }
  2834. else
  2835. {
  2836. DiscordChannelDeleteEventArgs e = new DiscordChannelDeleteEventArgs { ChannelDeleted = GetChannelByID(message["d"]["id"].ToObject<long>()) };
  2837. DiscordServer server;
  2838. server = e.ChannelDeleted.Parent;
  2839. server.Channels.Remove(server.Channels.Find(x => x.ID == e.ChannelDeleted.ID));
  2840.  
  2841. if (ChannelDeleted != null)
  2842. ChannelDeleted(this, e);
  2843. }
  2844. }
  2845.  
  2846. private void ChannelUpdateEvents(JObject message)
  2847. {
  2848. DiscordChannelUpdateEventArgs e = new DiscordChannelUpdateEventArgs();
  2849. e.RawJson = message;
  2850. DiscordChannel oldChannel = ServersList.Find(x => x.Channels.Find(y => y.ID == message["d"]["id"].ToString()) != null).Channels.Find(x => x.ID == message["d"]["id"].ToString());
  2851. e.OldChannel = oldChannel.ShallowCopy();
  2852. DiscordChannel newChannel = oldChannel;
  2853. newChannel.Name = message["d"]["name"].ToString();
  2854. if (!message["d"]["topic"].IsNullOrEmpty())
  2855. newChannel.Topic = message["d"]["topic"].ToString();
  2856. else
  2857. newChannel.Topic = oldChannel.Topic;
  2858.  
  2859. newChannel.Private = message["d"]["is_private"].ToObject<bool>();
  2860.  
  2861. List<DiscordPermissionOverride> permissionoverrides = new List<DiscordPermissionOverride>();
  2862. foreach (var o in message["d"]["permission_overwrites"])
  2863. {
  2864. DiscordPermissionOverride dpo = new DiscordPermissionOverride(o["allow"].ToObject<uint>(), o["deny"].ToObject<uint>());
  2865. dpo.type = o["type"].ToObject<DiscordPermissionOverride.OverrideType>();
  2866. dpo.id = o["id"].ToString();
  2867.  
  2868. permissionoverrides.Add(dpo);
  2869. }
  2870. newChannel.PermissionOverrides = permissionoverrides;
  2871.  
  2872. e.NewChannel = newChannel;
  2873.  
  2874. DiscordServer serverToRemoveFrom = ServersList.Find(x => x.Channels.Find(y => y.ID == newChannel.ID) != null);
  2875. newChannel.Parent = serverToRemoveFrom;
  2876. int indexOfServer = ServersList.IndexOf(serverToRemoveFrom);
  2877. serverToRemoveFrom.Channels.Remove(oldChannel);
  2878. serverToRemoveFrom.Channels.Add(newChannel);
  2879.  
  2880. ServersList.RemoveAt(indexOfServer);
  2881. ServersList.Insert(indexOfServer, serverToRemoveFrom);
  2882.  
  2883. if (ChannelUpdated != null)
  2884. ChannelUpdated(this, e);
  2885. }
  2886.  
  2887. private void GuildDeleteEvents(JObject message)
  2888. {
  2889. DiscordGuildDeleteEventArgs e = new DiscordGuildDeleteEventArgs();
  2890. e.Server = ServersList.Find(x => x.ID == message["d"]["id"].ToString());
  2891. e.RawJson = message;
  2892. ServersList.Remove(e.Server);
  2893. if (GuildDeleted != null)
  2894. GuildDeleted(this, e);
  2895. }
  2896.  
  2897. private void GuildCreateEvents(JObject message)
  2898. {
  2899. DiscordGuildCreateEventArgs e = new DiscordGuildCreateEventArgs();
  2900. e.RawJson = message;
  2901. DiscordServer server = new DiscordServer();
  2902. server.JoinedAt = message["d"]["joined_at"].ToObject<DateTime>();
  2903. server.parentclient = this;
  2904. server.ID = message["d"]["id"].ToString();
  2905. server.Name = message["d"]["name"].ToString();
  2906. server.Members = new Dictionary<ID, DiscordMember>();
  2907. server.Channels = new List<DiscordChannel>();
  2908. server.Roles = new List<DiscordRole>();
  2909. foreach (var roll in message["d"]["roles"])
  2910. {
  2911. DiscordRole t = new DiscordRole
  2912. {
  2913. Color = new DiscordSharp.Color(roll["color"].ToObject<int>().ToString("x")),
  2914. Name = roll["name"].ToString(),
  2915. Permissions = new DiscordPermission(roll["permissions"].ToObject<uint>()),
  2916. Position = roll["position"].ToObject<int>(),
  2917. Managed = roll["managed"].ToObject<bool>(),
  2918. ID = roll["id"].ToString(),
  2919. Hoist = roll["hoist"].ToObject<bool>()
  2920. };
  2921. server.Roles.Add(t);
  2922. }
  2923. foreach (var chn in message["d"]["channels"])
  2924. {
  2925. DiscordChannel tempChannel = new DiscordChannel();
  2926. tempChannel.Client = this;
  2927. tempChannel.ID = chn["id"].ToString();
  2928. tempChannel.Type = chn["type"].ToObject<ChannelType>();
  2929.  
  2930. if (!chn["topic"].IsNullOrEmpty())
  2931. tempChannel.Topic = chn["topic"].ToString();
  2932. if (tempChannel.Type == ChannelType.Voice && !chn["bitrate"].IsNullOrEmpty())
  2933. tempChannel.Bitrate = chn["bitrate"].ToObject<int>();
  2934.  
  2935. tempChannel.Name = chn["name"].ToString();
  2936. tempChannel.Private = false;
  2937. tempChannel.PermissionOverrides = new List<DiscordPermissionOverride>();
  2938. tempChannel.Parent = server;
  2939. foreach (var o in chn["permission_overwrites"])
  2940. {
  2941. if (tempChannel.Type == ChannelType.Voice)
  2942. continue;
  2943. DiscordPermissionOverride dpo = new DiscordPermissionOverride(o["allow"].ToObject<uint>(), o["deny"].ToObject<uint>());
  2944. dpo.id = o["id"].ToString();
  2945.  
  2946. if (o["type"].ToString() == "member")
  2947. dpo.type = DiscordPermissionOverride.OverrideType.member;
  2948. else
  2949. dpo.type = DiscordPermissionOverride.OverrideType.role;
  2950.  
  2951. tempChannel.PermissionOverrides.Add(dpo);
  2952. }
  2953. server.Channels.Add(tempChannel);
  2954. }
  2955. foreach (var mbr in message["d"]["members"])
  2956. {
  2957. DiscordMember member = JsonConvert.DeserializeObject<DiscordMember>(mbr["user"].ToString());
  2958. if(mbr["nick"] != null)
  2959. member.Nickname = mbr["nick"].ToString();
  2960.  
  2961. member.parentclient = this;
  2962. member.Parent = server;
  2963.  
  2964. foreach (var rollid in mbr["roles"])
  2965. {
  2966. member.Roles.Add(server.Roles.Find(x => x.ID == rollid.ToString()));
  2967. }
  2968. if (member.Roles.Count == 0)
  2969. member.Roles.Add(server.Roles.Find(x => x.Name == "@everyone"));
  2970. server.AddMember(member);
  2971. }
  2972. server.Owner = server.GetMemberByKey(message["d"]["owner_id"].ToString());
  2973. e.Server = server;
  2974.  
  2975. if (!message["d"]["unavailable"].IsNullOrEmpty() && message["d"]["unavailable"].ToObject<bool>() == false)
  2976. {
  2977. var oldServer = ServersList.Find(x => x.ID == server.ID);
  2978. if (oldServer != null && oldServer.Unavailable)
  2979. ServersList.Remove(oldServer);
  2980.  
  2981. ServersList.Add(server);
  2982.  
  2983. DebugLogger.Log($"Guild with ID {server.ID} ({server.Name}) became available.");
  2984. GuildAvailable?.Invoke(this, e);
  2985. return;
  2986. }
  2987.  
  2988. ServersList.Add(server);
  2989. GuildCreated?.Invoke(this, e);
  2990. }
  2991.  
  2992. private void GuildMemberAddEvents(JObject message)
  2993. {
  2994. DiscordGuildMemberAddEventArgs e = new DiscordGuildMemberAddEventArgs();
  2995. e.RawJson = message;
  2996. e.Guild = ServersList.Find(x => x.ID == message["d"]["guild_id"].ToString());
  2997.  
  2998. DiscordMember existingMember = e.Guild.GetMemberByKey(message["d"]["user"]["id"].ToString());
  2999. if (existingMember != null)
  3000. {
  3001.  
  3002. DiscordMember newMember = JsonConvert.DeserializeObject<DiscordMember>(message["d"]["user"].ToString());
  3003. newMember.parentclient = this;
  3004. e.AddedMember = newMember;
  3005. newMember.Parent = e.Guild;
  3006. e.Roles = message["d"]["roles"].ToObject<string[]>();
  3007. e.JoinedAt = DateTime.Parse(message["d"]["joined_at"].ToString());
  3008.  
  3009. ServersList.Find(x => x == e.Guild).AddMember(newMember);
  3010. if (UserAddedToServer != null)
  3011. UserAddedToServer(this, e);
  3012. }
  3013. else
  3014. {
  3015. DebugLogger.Log($"Skipping guild member add because user already exists in server.", MessageLevel.Debug);
  3016. }
  3017. }
  3018. private void GuildMemberRemoveEvents(JObject message)
  3019. {
  3020. DiscordGuildMemberRemovedEventArgs e = new DiscordGuildMemberRemovedEventArgs();
  3021. DiscordMember removed = new DiscordMember(this);
  3022. removed.parentclient = this;
  3023.  
  3024. List<DiscordMember> membersToRemove = new List<DiscordMember>();
  3025. foreach (var server in ServersList)
  3026. {
  3027. if (server.ID != message["d"]["guild_id"].ToString())
  3028. continue;
  3029. foreach (var member in server.Members)
  3030. {
  3031. if (member.Value.ID == message["d"]["user"]["id"].ToString())
  3032. {
  3033. removed = member.Value;
  3034. membersToRemove.Add(removed);
  3035. RemovedMembers.Add(removed);
  3036. }
  3037. }
  3038. }
  3039.  
  3040. foreach (var member in membersToRemove)
  3041. {
  3042. foreach (var server in ServersList)
  3043. {
  3044. try
  3045. {
  3046. server.RemoveMember(member.ID);
  3047. }
  3048. catch { } //oh, you mean useless?
  3049. }
  3050. }
  3051. e.MemberRemoved = removed;
  3052. e.Server = ServersList.Find(x => x.ID == message["d"]["guild_id"].ToString());
  3053. e.RawJson = message;
  3054.  
  3055. if (UserRemovedFromServer != null)
  3056. UserRemovedFromServer(this, e);
  3057. }
  3058.  
  3059. private void UserUpdateEvents(JObject message)
  3060. {
  3061. DiscordUserUpdateEventArgs e = new DiscordUserUpdateEventArgs();
  3062. e.RawJson = message;
  3063. DiscordMember newMember = JsonConvert.DeserializeObject<DiscordMember>(message["d"].ToString());
  3064. newMember.parentclient = this;
  3065.  
  3066. DiscordMember oldMember = new DiscordMember(this);
  3067. oldMember.parentclient = this;
  3068. //Update members
  3069. foreach (var server in ServersList)
  3070. {
  3071. foreach (var member in server.Members)
  3072. {
  3073. if (member.Value.ID == newMember.ID)
  3074. {
  3075. oldMember = member.Value;
  3076. server.AddMember(newMember);
  3077. break;
  3078. }
  3079. }
  3080. }
  3081.  
  3082. newMember.Parent = oldMember.Parent;
  3083.  
  3084. if (!message["roles"].IsNullOrEmpty())
  3085. {
  3086. JArray rawRoles = JArray.Parse(message["roles"].ToString());
  3087. if (rawRoles.Count > 0)
  3088. {
  3089. foreach (var role in rawRoles.Children())
  3090. {
  3091. newMember.Roles.Add(newMember.Parent.Roles.Find(x => x.ID == role.ToString()));
  3092. }
  3093. }
  3094. else
  3095. {
  3096. newMember.Roles.Add(newMember.Parent.Roles.Find(x => x.Name == "@everyone"));
  3097. }
  3098. }
  3099.  
  3100. e.NewMember = newMember;
  3101. e.OriginalMember = oldMember;
  3102. UserUpdate?.Invoke(this, e);
  3103. }
  3104.  
  3105. private void MessageDeletedEvents(JObject message)
  3106. {
  3107. DiscordMessageDeletedEventArgs e = new DiscordMessageDeletedEventArgs();
  3108. e.DeletedMessage = FindInMessageLog(message["d"]["id"].ToString());
  3109.  
  3110. DiscordServer inServer;
  3111. inServer = ServersList.Find(x => x.Channels.Find(y => y.ID == message["d"]["channel_id"].ToString()) != null);
  3112. if (inServer == null) //dm delete
  3113. {
  3114. DiscordPrivateMessageDeletedEventArgs dm = new DiscordPrivateMessageDeletedEventArgs();
  3115. dm.DeletedMessage = e.DeletedMessage;
  3116. dm.RawJson = message;
  3117. dm.Channel = PrivateChannels.Find(x => x.ID == message["d"]["channel_id"].ToString());
  3118. if (PrivateMessageDeleted != null)
  3119. PrivateMessageDeleted(this, dm);
  3120. }
  3121. else
  3122. {
  3123. e.Channel = inServer.Channels.Find(x => x.ID == message["d"]["channel_id"].ToString());
  3124. e.RawJson = message;
  3125. }
  3126.  
  3127. if (MessageDeleted != null)
  3128. MessageDeleted(this, e);
  3129. }
  3130.  
  3131. private void VoiceStateUpdateEvents(JObject message)
  3132. {
  3133. var f = message["d"]["channel_id"];
  3134. if (f.ToString() == null)
  3135. {
  3136. DiscordLeftVoiceChannelEventArgs le = new DiscordLeftVoiceChannelEventArgs();
  3137. DiscordServer inServer = ServersList.Find(x => x.ID == message["d"]["guild_id"].ToString());
  3138. le.User = inServer.GetMemberByKey(message["d"]["user_id"].ToString());
  3139. le.Guild = inServer;
  3140. le.RawJson = message;
  3141.  
  3142. if (VoiceClient != null && VoiceClient.Connected)
  3143. VoiceClient.MemberRemoved(le.User);
  3144. if (UserLeftVoiceChannel != null)
  3145. UserLeftVoiceChannel(this, le);
  3146. return;
  3147. }
  3148. DiscordVoiceStateUpdateEventArgs e = new DiscordVoiceStateUpdateEventArgs();
  3149. e.Guild = ServersList.Find(x => x.ID == message["d"]["guild_id"].ToString());
  3150. DiscordMember memberToUpdate = e.Guild.GetMemberByKey(message["d"]["user_id"].ToString());
  3151. if (memberToUpdate != null)
  3152. {
  3153. e.Channel = e.Guild.Channels.Find(x => x.ID == message["d"]["channel_id"].ToString());
  3154. memberToUpdate.CurrentVoiceChannel = e.Channel;
  3155. if (!message["d"]["self_deaf"].IsNullOrEmpty())
  3156. e.SelfDeaf = message["d"]["self_deaf"].ToObject<bool>();
  3157. e.Deaf = message["d"]["deaf"].ToObject<bool>();
  3158. if (!message["d"]["self_mute"].IsNullOrEmpty())
  3159. e.SelfMute = message["d"]["self_mute"].ToObject<bool>();
  3160. e.Mute = message["d"]["mute"].ToObject<bool>();
  3161. memberToUpdate.Muted = e.Mute;
  3162. e.Suppress = message["d"]["suppress"].ToObject<bool>();
  3163. memberToUpdate.Deaf = e.Suppress;
  3164. e.RawJson = message;
  3165.  
  3166. e.User = memberToUpdate;
  3167.  
  3168. if (VoiceClient != null && VoiceClient.Connected)
  3169. VoiceClient.MemberAdded(e.User);
  3170.  
  3171. if (!message["d"]["session_id"].IsNullOrEmpty()) //then this has to do with you
  3172. {
  3173. if (e.User.ID == Me.ID)
  3174. {
  3175. Me.Muted = e.SelfMute;
  3176. Me.Deaf = e.SelfDeaf;
  3177. if (VoiceClient != null)
  3178. VoiceClient.SessionID = message["d"]["session_id"].ToString();
  3179. }
  3180. }
  3181.  
  3182. if (VoiceStateUpdate != null)
  3183. VoiceStateUpdate(this, e);
  3184. }
  3185. }
  3186. private JObject ServerInfo(string channelOrServerId)
  3187. {
  3188. string url = Endpoints.BaseAPI + Endpoints.Guilds + $"/{channelOrServerId}";
  3189. try
  3190. {
  3191. return JObject.Parse(WebWrapper.Get(url, token));
  3192. }
  3193. catch (Exception ex)
  3194. {
  3195. DebugLogger.Log("(Private) Error ocurred while retrieving server info: " + ex.Message, MessageLevel.Error);
  3196. }
  3197. return null;
  3198. }
  3199.  
  3200. private int HeartbeatInterval = 41250;
  3201. private static DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
  3202. private void KeepAlive()
  3203. {
  3204. //string keepAliveJson = "{\"op\":" + Opcodes.HEARTBEAT + ", \"d\":" + Sequence + "}";
  3205. string keepAliveJson = JsonConvert.SerializeObject(new
  3206. {
  3207. op = Opcodes.HEARTBEAT,
  3208. d = Sequence
  3209. });
  3210. if (ws != null)
  3211. {
  3212. ws.Send(keepAliveJson);
  3213. KeepAliveSent?.Invoke(this, new DiscordKeepAliveSentEventArgs { SentAt = DateTime.Now, JsonSent = keepAliveJson });
  3214. }
  3215. }
  3216.  
  3217. /// <summary>
  3218. /// Disposes.
  3219. /// </summary>
  3220. public void Dispose()
  3221. {
  3222. try
  3223. {
  3224. KeepAliveTaskTokenSource.Cancel();
  3225. ws.Close();
  3226. ws = null;
  3227. ServersList = null;
  3228. PrivateChannels = null;
  3229. Me = null;
  3230. token = null;
  3231. this.ClientPrivateInformation = null;
  3232. }
  3233. catch { /*already been disposed elsewhere */ }
  3234. }
  3235.  
  3236. /// <summary>
  3237. /// Logs out of Discord and then disposes.
  3238. /// </summary>
  3239. public void Logout()
  3240. {
  3241. string url = Endpoints.BaseAPI + Endpoints.Auth + "/logout";
  3242. string msg = JsonConvert.SerializeObject(new { token = token });
  3243. WebWrapper.Post(url, msg);
  3244. Dispose();
  3245. }
  3246.  
  3247. /// <summary>
  3248. /// Sends a login request.
  3249. /// </summary>
  3250. /// <returns>The token if login was succesful, or null if not</returns>
  3251. public string SendLoginRequest()
  3252. {
  3253. if (token == null) //no token override provided, need to read token
  3254. {
  3255. if (String.IsNullOrEmpty(ClientPrivateInformation.Email))
  3256. {
  3257. throw new ArgumentNullException("Email was null/invalid!");
  3258. }
  3259. StrippedEmail = ClientPrivateInformation.Email.Replace('@', '_').Replace('.', '_'); //strips characters from email for hashing
  3260.  
  3261. if (File.Exists(StrippedEmail.GetHashCode() + ".cache"))
  3262. {
  3263. string read = "";
  3264. using (var sr = new StreamReader(StrippedEmail.GetHashCode() + ".cache"))
  3265. {
  3266. read = sr.ReadLine();
  3267. if (read.StartsWith("#")) //comment
  3268. {
  3269. token = sr.ReadLine();
  3270. DebugLogger.Log("Loading token from cache.");
  3271. }
  3272. token = token.Trim(); //trim any excess whitespace
  3273. }
  3274. }
  3275. else
  3276. {
  3277. if (ClientPrivateInformation == null || ClientPrivateInformation.Email == null || ClientPrivateInformation.Password == null)
  3278. throw new ArgumentNullException("You didn't supply login information!");
  3279. string url = Endpoints.BaseAPI + Endpoints.Auth + Endpoints.Login;
  3280. string msg = JsonConvert.SerializeObject(new
  3281. {
  3282. email = ClientPrivateInformation.Email,
  3283. password = ClientPrivateInformation.Password
  3284. });
  3285. DebugLogger.Log("No token present, sending login request..");
  3286. var result = JObject.Parse(WebWrapper.Post(url, msg));
  3287.  
  3288. if (result["token"].IsNullOrEmpty())
  3289. {
  3290. string message = "Failed to login to Discord.";
  3291. if (!result["email"].IsNullOrEmpty())
  3292. message += " Email was invalid: " + result["email"];
  3293. if (!result["password"].IsNullOrEmpty())
  3294. message += " password was invalid: " + result["password"];
  3295.  
  3296. throw new DiscordLoginException(message);
  3297. }
  3298. token = result["token"].ToString();
  3299.  
  3300. using (var sw = new StreamWriter(StrippedEmail.GetHashCode() + ".cache"))
  3301. {
  3302. sw.WriteLine($"#Token cache for {ClientPrivateInformation.Email}");
  3303. sw.WriteLine(token);
  3304. DebugLogger.Log($"{StrippedEmail.GetHashCode()}.cache written!");
  3305. }
  3306. }
  3307. }
  3308. return token;
  3309. }
  3310. }
  3311. }
Add Comment
Please, Sign In to add comment