VoteCommands.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. using System.Linq;
  2. using Content.Server.Administration;
  3. using Content.Server.Administration.Logs;
  4. using Content.Server.Chat.Managers;
  5. using Content.Server.Discord.WebhookMessages;
  6. using Content.Server.Voting.Managers;
  7. using Content.Shared.Administration;
  8. using Content.Shared.CCVar;
  9. using Content.Shared.Database;
  10. using Content.Shared.Voting;
  11. using Robust.Server;
  12. using Robust.Shared.Configuration;
  13. using Robust.Shared.Console;
  14. using Robust.Shared.Utility;
  15. namespace Content.Server.Voting
  16. {
  17. [AnyCommand]
  18. public sealed class CreateVoteCommand : IConsoleCommand
  19. {
  20. [Dependency] private readonly IAdminLogManager _adminLogger = default!;
  21. public string Command => "createvote";
  22. public string Description => Loc.GetString("cmd-createvote-desc");
  23. public string Help => Loc.GetString("cmd-createvote-help");
  24. public void Execute(IConsoleShell shell, string argStr, string[] args)
  25. {
  26. if (args.Length != 1 && args[0] != StandardVoteType.Votekick.ToString())
  27. {
  28. shell.WriteError(Loc.GetString("shell-need-exactly-one-argument"));
  29. return;
  30. }
  31. if (args.Length != 3 && args[0] == StandardVoteType.Votekick.ToString())
  32. {
  33. shell.WriteError(Loc.GetString("shell-wrong-arguments-number-need-specific", ("properAmount", 3), ("currentAmount", args.Length)));
  34. return;
  35. }
  36. if (!Enum.TryParse<StandardVoteType>(args[0], ignoreCase: true, out var type))
  37. {
  38. shell.WriteError(Loc.GetString("cmd-createvote-invalid-vote-type"));
  39. return;
  40. }
  41. var mgr = IoCManager.Resolve<IVoteManager>();
  42. if (shell.Player != null && !mgr.CanCallVote(shell.Player, type))
  43. {
  44. _adminLogger.Add(LogType.Vote, LogImpact.Medium, $"{shell.Player} failed to start {type.ToString()} vote");
  45. shell.WriteError(Loc.GetString("cmd-createvote-cannot-call-vote-now"));
  46. return;
  47. }
  48. mgr.CreateStandardVote(shell.Player, type, args.Skip(1).ToArray());
  49. }
  50. public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
  51. {
  52. if (args.Length == 1)
  53. {
  54. var options = Enum.GetNames<StandardVoteType>();
  55. return CompletionResult.FromHintOptions(options, Loc.GetString("cmd-createvote-arg-vote-type"));
  56. }
  57. return CompletionResult.Empty;
  58. }
  59. }
  60. [AdminCommand(AdminFlags.Moderator)]
  61. public sealed class CreateCustomCommand : LocalizedEntityCommands
  62. {
  63. [Dependency] private readonly IVoteManager _voteManager = default!;
  64. [Dependency] private readonly IAdminLogManager _adminLogger = default!;
  65. [Dependency] private readonly IChatManager _chatManager = default!;
  66. [Dependency] private readonly VoteWebhooks _voteWebhooks = default!;
  67. [Dependency] private readonly IConfigurationManager _cfg = default!;
  68. private ISawmill _sawmill = default!;
  69. private const int MaxArgCount = 10;
  70. public override string Command => "customvote";
  71. public override void Execute(IConsoleShell shell, string argStr, string[] args)
  72. {
  73. _sawmill = Logger.GetSawmill("vote");
  74. if (args.Length < 3 || args.Length > MaxArgCount)
  75. {
  76. shell.WriteError(Loc.GetString("shell-need-between-arguments",("lower", 3), ("upper", 10)));
  77. return;
  78. }
  79. var title = args[0];
  80. var options = new VoteOptions
  81. {
  82. Title = title,
  83. Duration = TimeSpan.FromSeconds(30),
  84. };
  85. for (var i = 1; i < args.Length; i++)
  86. {
  87. options.Options.Add((args[i], i));
  88. }
  89. options.SetInitiatorOrServer(shell.Player);
  90. if (shell.Player != null)
  91. _adminLogger.Add(LogType.Vote, LogImpact.Medium, $"{shell.Player} initiated a custom vote: {options.Title} - {string.Join("; ", options.Options.Select(x => x.text))}");
  92. else
  93. _adminLogger.Add(LogType.Vote, LogImpact.Medium, $"Initiated a custom vote: {options.Title} - {string.Join("; ", options.Options.Select(x => x.text))}");
  94. var vote = _voteManager.CreateVote(options);
  95. var webhookState = _voteWebhooks.CreateWebhookIfConfigured(options, _cfg.GetCVar(CCVars.DiscordVoteWebhook));
  96. vote.OnFinished += (_, eventArgs) =>
  97. {
  98. if (eventArgs.Winner == null)
  99. {
  100. var ties = string.Join(", ", eventArgs.Winners.Select(c => args[(int) c]));
  101. _adminLogger.Add(LogType.Vote, LogImpact.Medium, $"Custom vote {options.Title} finished as tie: {ties}");
  102. _chatManager.DispatchServerAnnouncement(Loc.GetString("cmd-customvote-on-finished-tie", ("ties", ties)));
  103. }
  104. else
  105. {
  106. _adminLogger.Add(LogType.Vote, LogImpact.Medium, $"Custom vote {options.Title} finished: {args[(int) eventArgs.Winner]}");
  107. _chatManager.DispatchServerAnnouncement(Loc.GetString("cmd-customvote-on-finished-win", ("winner", args[(int) eventArgs.Winner])));
  108. }
  109. _voteWebhooks.UpdateWebhookIfConfigured(webhookState, eventArgs);
  110. };
  111. vote.OnCancelled += _ =>
  112. {
  113. _voteWebhooks.UpdateCancelledWebhookIfConfigured(webhookState);
  114. };
  115. }
  116. public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
  117. {
  118. if (args.Length == 1)
  119. return CompletionResult.FromHint(Loc.GetString("cmd-customvote-arg-title"));
  120. if (args.Length > MaxArgCount)
  121. return CompletionResult.Empty;
  122. var n = args.Length - 1;
  123. return CompletionResult.FromHint(Loc.GetString("cmd-customvote-arg-option-n", ("n", n)));
  124. }
  125. }
  126. [AnyCommand]
  127. public sealed class VoteCommand : IConsoleCommand
  128. {
  129. public string Command => "vote";
  130. public string Description => Loc.GetString("cmd-vote-desc");
  131. public string Help => Loc.GetString("cmd-vote-help");
  132. public void Execute(IConsoleShell shell, string argStr, string[] args)
  133. {
  134. if (shell.Player == null)
  135. {
  136. shell.WriteError(Loc.GetString("cmd-vote-on-execute-error-must-be-player"));
  137. return;
  138. }
  139. if (args.Length != 2)
  140. {
  141. shell.WriteError(Loc.GetString("shell-wrong-arguments-number-need-specific", ("properAmount", 2), ("currentAmount", args.Length)));
  142. return;
  143. }
  144. if (!int.TryParse(args[0], out var voteId))
  145. {
  146. shell.WriteError(Loc.GetString("cmd-vote-on-execute-error-invalid-vote-id"));
  147. return;
  148. }
  149. if (!int.TryParse(args[1], out var voteOption))
  150. {
  151. shell.WriteError(Loc.GetString("cmd-vote-on-execute-error-invalid-vote-options"));
  152. return;
  153. }
  154. var mgr = IoCManager.Resolve<IVoteManager>();
  155. if (!mgr.TryGetVote(voteId, out var vote))
  156. {
  157. shell.WriteError(Loc.GetString("cmd-vote-on-execute-error-invalid-vote"));
  158. return;
  159. }
  160. int? optionN;
  161. if (voteOption == -1)
  162. {
  163. optionN = null;
  164. }
  165. else if (vote.IsValidOption(voteOption))
  166. {
  167. optionN = voteOption;
  168. }
  169. else
  170. {
  171. shell.WriteError(Loc.GetString("cmd-vote-on-execute-error-invalid-option"));
  172. return;
  173. }
  174. vote.CastVote(shell.Player!, optionN);
  175. }
  176. }
  177. [AnyCommand]
  178. public sealed class ListVotesCommand : IConsoleCommand
  179. {
  180. public string Command => "listvotes";
  181. public string Description => Loc.GetString("cmd-listvotes-desc");
  182. public string Help => Loc.GetString("cmd-listvotes-help");
  183. public void Execute(IConsoleShell shell, string argStr, string[] args)
  184. {
  185. var mgr = IoCManager.Resolve<IVoteManager>();
  186. foreach (var vote in mgr.ActiveVotes)
  187. {
  188. shell.WriteLine($"[{vote.Id}] {vote.InitiatorText}: {vote.Title}");
  189. }
  190. }
  191. }
  192. [AdminCommand(AdminFlags.Moderator)]
  193. public sealed class CancelVoteCommand : IConsoleCommand
  194. {
  195. [Dependency] private readonly IAdminLogManager _adminLogger = default!;
  196. public string Command => "cancelvote";
  197. public string Description => Loc.GetString("cmd-cancelvote-desc");
  198. public string Help => Loc.GetString("cmd-cancelvote-help");
  199. public void Execute(IConsoleShell shell, string argStr, string[] args)
  200. {
  201. var mgr = IoCManager.Resolve<IVoteManager>();
  202. if (args.Length < 1)
  203. {
  204. shell.WriteError(Loc.GetString("cmd-cancelvote-error-missing-vote-id"));
  205. return;
  206. }
  207. if (!int.TryParse(args[0], out var id) || !mgr.TryGetVote(id, out var vote))
  208. {
  209. shell.WriteError(Loc.GetString("cmd-cancelvote-error-invalid-vote-id"));
  210. return;
  211. }
  212. if (shell.Player != null)
  213. _adminLogger.Add(LogType.Vote, LogImpact.Medium, $"{shell.Player} canceled vote: {vote.Title}");
  214. else
  215. _adminLogger.Add(LogType.Vote, LogImpact.Medium, $"Canceled vote: {vote.Title}");
  216. vote.Cancel();
  217. }
  218. public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
  219. {
  220. var mgr = IoCManager.Resolve<IVoteManager>();
  221. if (args.Length == 1)
  222. {
  223. var options = mgr.ActiveVotes
  224. .OrderBy(v => v.Id)
  225. .Select(v => new CompletionOption(v.Id.ToString(), v.Title));
  226. return CompletionResult.FromHintOptions(options, Loc.GetString("cmd-cancelvote-arg-id"));
  227. }
  228. return CompletionResult.Empty;
  229. }
  230. }
  231. }