You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Program.cs 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Reflection;
  6. using System.Threading.Tasks;
  7. using RhinoReminds.Utilities;
  8. using S22.Xmpp;
  9. using S22.Xmpp.Client;
  10. using S22.Xmpp.Im;
  11. using SBRL.Utilities;
  12. namespace RhinoReminds
  13. {
  14. public class ProgramSettings
  15. {
  16. public string AllowedDomain = "*";
  17. public string Filepath = "./reminders.xml";
  18. public string Jid = null;
  19. public string AvatarFilepath = string.Empty;
  20. public string PidFile = null;
  21. public bool ExitOnModify = false;
  22. public string Password = null;
  23. }
  24. public static class Program
  25. {
  26. public static string Version {
  27. get {
  28. return $"v{Assembly.GetExecutingAssembly().GetName().Version}" +
  29. $"-{EmbeddedFiles.ReadAllText("RhinoReminds.git-hash.txt").Substring(0, 7)}";
  30. }
  31. }
  32. private static ProgramSettings settings = new ProgramSettings();
  33. public static int Main(string[] args)
  34. {
  35. List<string> extras = new List<string>();
  36. for (int i = 0; i < args.Length; i++)
  37. {
  38. if (!args[i].StartsWith("-")) {
  39. extras.Add(args[i]);
  40. continue;
  41. }
  42. switch (args[i]) {
  43. case "-h":
  44. case "--help":
  45. Console.WriteLine($"--- RhinoReminds {Version} ---");
  46. Console.WriteLine("> An XMPP reminder bot");
  47. Console.WriteLine(" By Starbeamrainbowlabs");
  48. Console.WriteLine();
  49. Console.WriteLine("Usage:");
  50. Console.WriteLine(" mono RhinoReminds.exe {options}");
  51. Console.WriteLine();
  52. Console.WriteLine("Options:");
  53. Console.WriteLine(" -h --help Show this message");
  54. Console.WriteLine($" -f --file Specify where to save reminders (default: {settings.Filepath})");
  55. Console.WriteLine(" --domain {domain} Set the domain users are allowed to originate at. Defaults to any domain.");
  56. Console.WriteLine(" --avatar Update the XMPP account's avatar to the specified image. By default the avatar is not updated.");
  57. Console.WriteLine(" --pidfile Save our process ID to the specified file, and delete it on exit");
  58. Console.WriteLine(" --auto-exit Watch for changes to the executable on disk and automatically quit with an exit code of 0 if a modification is detected. Useful for integration with a service manager and continuous deployment.");
  59. Console.WriteLine();
  60. Console.WriteLine("Environment Variables:");
  61. Console.WriteLine(" XMPP_JID The JID to login to");
  62. Console.WriteLine(" XMPP_PASSWORD The password to login with");
  63. return 0;
  64. case "--jid":
  65. settings.Jid = args[++i];
  66. break;
  67. case "-d":
  68. case "--domain":
  69. settings.AllowedDomain = args[++i];
  70. break;
  71. case "--avatar":
  72. settings.AvatarFilepath = args[++i];
  73. break;
  74. case "--pidfile":
  75. settings.PidFile = args[++i];
  76. break;
  77. case "--auto-exit":
  78. settings.ExitOnModify = true;
  79. break;
  80. default:
  81. Console.Error.WriteLine($"Error: Unknown argument '{args[i]}'.");
  82. return 14;
  83. }
  84. }
  85. settings.Jid = Environment.GetEnvironmentVariable("XMPP_JID");
  86. settings.Password = Environment.GetEnvironmentVariable("XMPP_PASSWORD");
  87. if (settings.Jid == null) {
  88. Console.Error.WriteLine("Error: No JID specified to login with.");
  89. Console.Error.WriteLine("Do so with the XMPP_JID environment variable!");
  90. return 15;
  91. }
  92. if (settings.Password == null) {
  93. Console.Error.WriteLine("Error: No password specified to login with.");
  94. Console.Error.WriteLine("Do so with the XMPP_PASSWORD environment variable!");
  95. return 16;
  96. }
  97. if (settings.PidFile != null)
  98. setupPidFile();
  99. run();
  100. // We shouldn't ever end up here, but just in case.....
  101. cleanupPidFile();
  102. return 0;
  103. }
  104. private static void setupPidFile()
  105. {
  106. File.WriteAllText(settings.PidFile, Process.GetCurrentProcess().Id.ToString());
  107. AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => cleanupPidFile();
  108. AppDomain.CurrentDomain.DomainUnload += (object sender, EventArgs e) => cleanupPidFile();
  109. }
  110. private static void cleanupPidFile() {
  111. // Make sure we only do cleanup once
  112. if (settings.PidFile == null)
  113. return;
  114. File.Delete(settings.PidFile);
  115. settings.PidFile = null;
  116. }
  117. private static void run()
  118. {
  119. Console.WriteLine("************************************");
  120. Console.WriteLine("***** RhinoReminds is starting *****");
  121. Console.WriteLine("************************************");
  122. Console.WriteLine($"[Program] Running {Version}");
  123. if (settings.ExitOnModify) {
  124. ExitWatcher.EnableExitOnModify();
  125. }
  126. ClientListener client = new ClientListener(settings.Jid, settings.Password) {
  127. ReminderFilePath = settings.Filepath
  128. };
  129. client.AllowedDomains.Add(settings.AllowedDomain);
  130. // Update the avatar if appropriate
  131. if (settings.AvatarFilepath != string.Empty) {
  132. OnConnectedHandler handler = null;
  133. handler = (object sender, OnConnectedEventArgs eventArgs) => {
  134. client.SetAvatar(settings.AvatarFilepath);
  135. Console.WriteLine($"[Program] Set avatar to '{settings.AvatarFilepath}'.");
  136. client.OnConnected -= handler;
  137. };
  138. client.OnConnected += handler;
  139. }
  140. // Connect to the server & start listening
  141. // Make sure the program doesn't exit whilst we're connected
  142. try {
  143. client.Start().Wait();
  144. } catch (Exception) {
  145. // Ensure we tidy up after ourselves by deleting the PID file
  146. if (settings.PidFile != null)
  147. cleanupPidFile();
  148. // Re-throw the error
  149. throw;
  150. }
  151. }
  152. }
  153. }