tgstation-server 6.19.0
The /tg/station 13 server suite
Loading...
Searching...
No Matches
ChatController.cs
Go to the documentation of this file.
1using System;
4using System.Linq;
9
13
28
30{
35#pragma warning disable CA1506 // TODO: Decomplexify
37 {
42
54 IDatabaseContext databaseContext,
55 IAuthenticationContext authenticationContext,
58 IApiHeadersProvider apiHeaders)
59 : base(
60 databaseContext,
61 authenticationContext,
62 logger,
64 apiHeaders)
65 {
67 }
68
76 {
78 {
79 IsAdminChannel = api.IsAdminChannel ?? false,
80 IsWatchdogChannel = api.IsWatchdogChannel ?? false,
81 IsUpdatesChannel = api.IsUpdatesChannel ?? false,
82 IsSystemChannel = api.IsSystemChannel ?? false,
83 Tag = api.Tag,
84 };
85
86 if (api.ChannelData != null)
87 {
88 switch (chatProvider)
89 {
90 case ChatProvider.Discord:
91 result.DiscordChannelId = UInt64.Parse(api.ChannelData, CultureInfo.InvariantCulture);
92 break;
93 case ChatProvider.Irc:
94 result.IrcChannel = api.ChannelData;
95 break;
96 default:
97 throw new InvalidOperationException($"Invalid chat provider: {chatProvider}");
98 }
99 }
100
101 return result;
102 }
103
111 [HttpPut]
114 {
115 ArgumentNullException.ThrowIfNull(model);
116
117 if (!model.Provider.HasValue)
118 return BadRequest(new ErrorMessageResponse(ErrorCode.ChatBotProviderMissing));
119
120 if (String.IsNullOrWhiteSpace(model.Name))
121 return BadRequest(new ErrorMessageResponse(ErrorCode.ChatBotWhitespaceName));
122
123 if (String.IsNullOrWhiteSpace(model.ConnectionString))
124 return BadRequest(new ErrorMessageResponse(ErrorCode.ChatBotWhitespaceConnectionString));
125
126 if (!model.ValidateProviderChannelTypes())
127 return BadRequest(new ErrorMessageResponse(ErrorCode.ChatBotWrongChannelType));
128
129 var newChannels = model.Channels?.Select(x => ConvertApiChatChannel(x, model.Provider!.Value)).ToList() ?? new List<Models.ChatChannel>(); // important that this isn't null
130
131 return await chatAuthority.InvokeTransformable<ChatBot, ChatBotResponse>(
132 this,
133 authority => authority.Create(
135 model.Name,
136 model.ConnectionString,
137 model.Provider.Value,
138 Instance.Require(x => x.Id),
139 model.ReconnectionInterval,
140 model.ChannelLimit,
141 model.Enabled ?? false,
142 cancellationToken));
143 }
144
152 [HttpDelete("{id}")]
155 public async ValueTask<IActionResult> Delete(long id, CancellationToken cancellationToken)
157 async instance =>
158 {
159 await Task.WhenAll(
160 instance.Chat.DeleteConnection(id, cancellationToken),
162 .ChatBots
163 .Where(x => x.Id == id)
164 .ExecuteDeleteAsync(cancellationToken));
165 return null;
166 })
167
168 ?? NoContent();
169
182 {
183 var connectionStrings = (AuthenticationContext.GetRight(RightsType.ChatBots) & (ulong)ChatBotRights.ReadConnectionString) != 0;
185 () => ValueTask.FromResult<PaginatableResult<ChatBot>?>(
188 .ChatBots
189 .Where(x => x.InstanceId == Instance.Id)
190 .Include(x => x.Channels)
191 .OrderBy(x => x.Id))),
192 chatBot =>
193 {
194 if (!connectionStrings)
195 chatBot.ConnectionString = null;
196
197 return ValueTask.CompletedTask;
198 },
199 page,
200 pageSize,
201 cancellationToken);
202 }
203
212 [HttpGet("{id}")]
216 public async ValueTask<IActionResult> GetId(long id, CancellationToken cancellationToken)
217 {
218 var query = DatabaseContext
219 .ChatBots
220 .Where(x => x.Id == id && x.InstanceId == Instance.Id)
221 .Include(x => x.Channels);
222
223 var results = await query.FirstOrDefaultAsync(cancellationToken);
224 if (results == default)
225 return this.Gone();
226
227 var connectionStrings = (AuthenticationContext.GetRight(RightsType.ChatBots) & (ulong)ChatBotRights.ReadConnectionString) != 0;
228
231
232 return Json(results.ToApi());
233 }
234
244 [HttpPost]
245 [TgsAuthorize(ChatBotRights.WriteChannels | ChatBotRights.WriteConnectionString | ChatBotRights.WriteEnabled | ChatBotRights.WriteName | ChatBotRights.WriteProvider)]
249#pragma warning disable CA1502, CA1506 // TODO: Decomplexify
251#pragma warning restore CA1502, CA1506
252 {
253 ArgumentNullException.ThrowIfNull(model);
254
256 if (earlyOut != null)
257 return earlyOut;
258
259 var query = DatabaseContext
260 .ChatBots
261 .Where(x => x.InstanceId == Instance.Id && x.Id == model.Id)
262 .Include(x => x.Channels);
263
264 var current = await query.FirstOrDefaultAsync(cancellationToken);
265
266 if (current == default)
267 return this.Gone();
268
269 if ((model.Channels?.Count ?? current.Channels!.Count) > (model.ChannelLimit ?? current.ChannelLimit!.Value))
270 {
271 // 400 or 409 depends on if the client sent both
272 var errorMessage = new ErrorMessageResponse(ErrorCode.ChatBotMaxChannels);
273 if (model.Channels != null && model.ChannelLimit.HasValue)
274 return BadRequest(errorMessage);
275 return Conflict(errorMessage);
276 }
277
279
280 bool anySettingsModified = false;
281
283 {
285 var property = (PropertyInfo)memberSelectorExpression.Member;
286
287 var newVal = property.GetValue(model);
288 if (newVal == null)
289 return false;
290 if (!userRights.HasFlag(requiredRight) && property.GetValue(current) != newVal)
291 return true;
292
293 property.SetValue(current, newVal);
294 anySettingsModified = true;
295 return false;
296 }
297
298 var oldProvider = current.Provider;
299
300 if (CheckModified(x => x.ConnectionString, ChatBotRights.WriteConnectionString)
301 || CheckModified(x => x.Enabled, ChatBotRights.WriteEnabled)
302 || CheckModified(x => x.Name, ChatBotRights.WriteName)
303 || CheckModified(x => x.Provider, ChatBotRights.WriteProvider)
304 || CheckModified(x => x.ReconnectionInterval, ChatBotRights.WriteReconnectionInterval)
305 || CheckModified(x => x.ChannelLimit, ChatBotRights.WriteChannelLimit)
306 || (model.Channels != null && !userRights.HasFlag(ChatBotRights.WriteChannels)))
307 return Forbid();
308
310 if (hasChannels || (model.Provider.HasValue && model.Provider != oldProvider))
311 {
312 DatabaseContext.ChatChannels.RemoveRange(current.Channels!);
313 if (hasChannels)
314 {
315 var dbChannels = model.Channels!.Select(x => ConvertApiChatChannel(x, model.Provider ?? current.Provider!.Value)).ToList();
318 }
319 else
320 current.Channels!.Clear();
321 }
322
323 await DatabaseContext.Save(cancellationToken);
324
326 async instance =>
327 {
328 var chat = instance.Chat;
330 await chat.ChangeSettings(current, cancellationToken); // have to rebuild the thing first
331
332 if ((model.Channels != null || anySettingsModified) && current.Enabled!.Value)
333 await chat.ChangeChannels(current.Id!.Value, current.Channels, cancellationToken);
334
335 return null;
336 });
337 if (earlyOut != null)
338 return earlyOut;
339
340 if (userRights.HasFlag(ChatBotRights.Read))
341 {
342 if (!userRights.HasFlag(ChatBotRights.ReadConnectionString))
343 current.ConnectionString = null;
344 return Json(current.ToApi());
345 }
346
347 return NoContent();
348 }
349
357 {
358 if (model.ReconnectionInterval == 0)
359 throw new InvalidOperationException("RecconnectionInterval cannot be zero!");
360
361 if (forCreation && !model.Provider.HasValue)
362 return BadRequest(new ErrorMessageResponse(ErrorCode.ChatBotProviderMissing));
363
364 if (model.Name != null && String.IsNullOrWhiteSpace(model.Name))
365 return BadRequest(new ErrorMessageResponse(ErrorCode.ChatBotWhitespaceName));
366
367 if (model.ConnectionString != null && String.IsNullOrWhiteSpace(model.ConnectionString))
368 return BadRequest(new ErrorMessageResponse(ErrorCode.ChatBotWhitespaceConnectionString));
369
370 if (!model.ValidateProviderChannelTypes())
371 return BadRequest(new ErrorMessageResponse(ErrorCode.ChatBotWrongChannelType));
372
373 var defaultMaxChannels = (ulong)Math.Max(ChatBot.DefaultChannelLimit, model.Channels?.Count ?? 0);
374 if (defaultMaxChannels > UInt16.MaxValue)
375 return BadRequest(new ErrorMessageResponse(ErrorCode.ChatBotMaxChannels));
376
377 if (forCreation)
379
380 return null;
381 }
382 }
383#pragma warning restore CA1506
384}
virtual ? long Id
The ID of the entity.
Definition EntityId.cs:14
Metadata about a server instance.
Definition Instance.cs:9
string? Tag
A custom tag users can define to group channels together.
Represents an error message returned by the server.
Routes to a server actions.
Definition Routes.cs:9
const string List
The postfix for list operations.
Definition Routes.cs:113
const string Chat
The chat bot controller.
Definition Routes.cs:93
ApiController for managing ChatBots.
static Models.ChatChannel ConvertApiChatChannel(Api.Models.ChatChannel api, ChatProvider chatProvider)
Converts api to a ChatChannel.
async ValueTask< IActionResult > Delete(long id, CancellationToken cancellationToken)
Delete a ChatBot.
async ValueTask< IActionResult > GetId(long id, CancellationToken cancellationToken)
Get a specific ChatBot.
ChatController(IRestAuthorityInvoker< IChatAuthority > chatAuthority, IDatabaseContext databaseContext, IAuthenticationContext authenticationContext, ILogger< ChatController > logger, IInstanceManager instanceManager, IApiHeadersProvider apiHeaders)
Initializes a new instance of the ChatController class.
readonly IRestAuthorityInvoker< IChatAuthority > chatAuthority
The IRestAuthorityInvoker<TAuthority> for the IChatAuthority.
BadRequestObjectResult? StandardModelChecks(ChatBotApiBase model, bool forCreation)
Perform some basic validation of a given model .
async ValueTask< IActionResult > Update([FromBody] ChatBotUpdateRequest model, CancellationToken cancellationToken)
Updates a chat bot model .
async ValueTask< IActionResult > Create([FromBody] ChatBotCreateRequest model, CancellationToken cancellationToken)
Create a new chat bot model .
ValueTask< IActionResult > List([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken)
List ChatBots.
async ValueTask< IActionResult?> WithComponentInstanceNullable(Func< IInstanceCore, ValueTask< IActionResult?> > action, Models.Instance? instance=null)
Run a given action with the relevant IInstance.
readonly IInstanceManager instanceManager
The IInstanceManager for the ComponentInterfacingController.
ComponentInterfacingController for operations that require an instance.
Backend abstract implementation of IDatabaseContext.
DbSet< ChatChannel > ChatChannels
The ChatChannels in the DatabaseContext.
Task Save(CancellationToken cancellationToken)
Saves changes made to the IDatabaseContext.A Task representing the running operation.
DbSet< ChatBot > ChatBots
The ChatBots in the DatabaseContext.
const ushort DefaultChannelLimit
Default for Api.Models.Internal.ChatBotSettings.ChannelLimit.
Definition ChatBot.cs:16
ulong GetRight(RightsType rightsType)
Get the value of a given rightsType .The value of rightsType . Note that if InstancePermissionSet is ...
Invokes TAuthority methods and generates IActionResult responses.
For creating and accessing authentication contexts.
ErrorCode
Types of Response.ErrorMessageResponses that the API may return.
Definition ErrorCode.cs:12
ChatProvider
Represents a chat service provider.
ChatBotRights
Rights for chat bots.
@ List
User may list files if the Models.Instance allows it.
RightsType
The type of rights a model uses.
Definition RightsType.cs:7
@ Api
The ApiHeaders.ApiVersionHeader header is missing or invalid.