|
5 | 5 | using EmbedIO.WebApi; |
6 | 6 | using Microsoft.Extensions.Logging; |
7 | 7 | using OpenShock.Desktop.ModuleBase.Api; |
| 8 | +using OpenShock.Desktop.ModuleBase.Models; |
8 | 9 |
|
9 | 10 | namespace OpenShock.Desktop.Modules.Interception.Server; |
10 | 11 |
|
11 | 12 | public sealed class PsWebApiController : WebApiController |
12 | 13 | { |
| 14 | + private static readonly JsonSerializerOptions JsonOptions = new() |
| 15 | + { |
| 16 | + PropertyNameCaseInsensitive = true |
| 17 | + }; |
| 18 | + |
13 | 19 | private readonly ILogger<PsWebApiController> _logger; |
| 20 | + private readonly IOpenShockControl _openShockControl; |
14 | 21 | private readonly IOpenShockData _openShockData; |
15 | 22 | private readonly InterceptionService _service; |
16 | 23 |
|
17 | | - public PsWebApiController(InterceptionService service, IOpenShockData openShockData, |
18 | | - ILogger<PsWebApiController> logger) |
| 24 | + public PsWebApiController(InterceptionService service, IOpenShockControl openShockControl, |
| 25 | + IOpenShockData openShockData, ILogger<PsWebApiController> logger) |
19 | 26 | { |
20 | 27 | _service = service; |
| 28 | + _openShockControl = openShockControl; |
21 | 29 | _openShockData = openShockData; |
22 | 30 | _logger = logger; |
23 | 31 | } |
24 | 32 |
|
| 33 | + [Route(HttpVerbs.Post, "/Operate")] |
| 34 | + public async Task Operate() |
| 35 | + { |
| 36 | + using var reader = new StreamReader(HttpContext.Request.InputStream); |
| 37 | + var body = await reader.ReadToEndAsync(); |
| 38 | + |
| 39 | + PiShockRequest? request; |
| 40 | + try |
| 41 | + { |
| 42 | + request = JsonSerializer.Deserialize<PiShockRequest>(body, JsonOptions); |
| 43 | + } |
| 44 | + catch |
| 45 | + { |
| 46 | + _logger.LogError("Error parsing JSON body: {Body}", body); |
| 47 | + HttpContext.Response.StatusCode = 400; |
| 48 | + await HttpContext.SendStringAsync("Invalid JSON", "text/plain", Encoding.UTF8); |
| 49 | + return; |
| 50 | + } |
| 51 | + |
| 52 | + if (request?.Code == null) |
| 53 | + { |
| 54 | + _logger.LogError("Missing share code in request: {Body}", body); |
| 55 | + HttpContext.Response.StatusCode = 400; |
| 56 | + await HttpContext.SendStringAsync("Missing share code", "text/plain", Encoding.UTF8); |
| 57 | + return; |
| 58 | + } |
| 59 | + |
| 60 | + if (!_service.Config.ShareCodeMappings.TryGetValue(request.Code, out var mapping)) |
| 61 | + { |
| 62 | + _logger.LogError("Share code not mapped to any shocker: {Code}", request.Code); |
| 63 | + await HttpContext.SendStringAsync("This code doesn't exist.", "text/plain", Encoding.UTF8); |
| 64 | + return; |
| 65 | + } |
| 66 | + |
| 67 | + if (mapping.ShockerIds.Count == 0) |
| 68 | + { |
| 69 | + _logger.LogError("Share code has no shockers configured: {Code}", request.Code); |
| 70 | + await HttpContext.SendStringAsync("This code doesn't exist.", "text/plain", Encoding.UTF8); |
| 71 | + return; |
| 72 | + } |
| 73 | + |
| 74 | + var controlType = request.Op switch |
| 75 | + { |
| 76 | + 0 => ControlType.Shock, |
| 77 | + 1 => ControlType.Vibrate, |
| 78 | + 2 => ControlType.Sound, |
| 79 | + _ => ControlType.Vibrate |
| 80 | + }; |
| 81 | + |
| 82 | + var durationMs = (ushort)Math.Clamp(request.Duration * 1000, mapping.MinDuration * 1000, mapping.MaxDuration * 1000); |
| 83 | + var intensity = (byte)Math.Clamp(request.Intensity, mapping.MinIntensity, mapping.MaxIntensity); |
| 84 | + |
| 85 | + if (request.Intensity <= 0) controlType = ControlType.Stop; |
| 86 | + |
| 87 | + var controls = mapping.ShockerIds.Select(id => new ShockerControl |
| 88 | + { |
| 89 | + Id = id, |
| 90 | + Type = controlType, |
| 91 | + Intensity = intensity, |
| 92 | + Duration = durationMs |
| 93 | + }).ToArray(); |
| 94 | + |
| 95 | + var customName = request.Name ?? request.Username ?? "PiShock Interception"; |
| 96 | + |
| 97 | + try |
| 98 | + { |
| 99 | + await _openShockControl.Control(controls, customName); |
| 100 | + _logger.LogInformation( |
| 101 | + "PiShock Ps API Operate: {ControlType} {Intensity}% for {Duration}s on {ShockerCount} shocker(s) by {Name}", |
| 102 | + controlType, intensity, durationMs / 1000.0, controls.Length, customName); |
| 103 | + await HttpContext.SendStringAsync("Operation Succeeded.", "text/plain", Encoding.UTF8); |
| 104 | + } |
| 105 | + catch (Exception ex) |
| 106 | + { |
| 107 | + HttpContext.Response.StatusCode = 500; |
| 108 | + await HttpContext.SendStringAsync(ex.Message, "text/plain", Encoding.UTF8); |
| 109 | + } |
| 110 | + } |
| 111 | + |
25 | 112 | [Route(HttpVerbs.Get, "/GetUserDevices")] |
26 | 113 | public async Task GetUserDevices() |
27 | 114 | { |
|
0 commit comments