Skip to content

Commit 594395b

Browse files
committed
Fixed a bug that made user-input rewards impossible to update.
Added Sentry error reporting using Streamer.bot dependency. Fixed chat config trigger. Fixed reported errors. New trigger : Kicks Gifted. Also added informations about automoderated messages.
1 parent 6cf7c59 commit 594395b

File tree

8 files changed

+427
-197
lines changed

8 files changed

+427
-197
lines changed

API/Events/ChatMessageDeletedEvent.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ public class ChatMessageDeletedEvent
2525
public string Id { get; internal set; }
2626
[JsonProperty("message")]
2727
public ChatMessageDeletedInfos Message { get; internal set; }
28+
[JsonProperty("aiModerated")]
29+
public bool AiModerated { get; internal set; }
30+
[JsonProperty("violatedRules")]
31+
public string[] ViolatedRules { get; internal set; } = {};
2832
}
2933

3034
public class ChatMessageDeletedInfos

API/Events/KicksGiftedEvent.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
Copyright (C) 2023-2025 Sehelitar
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU Affero General Public License as published
6+
by the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU Affero General Public License for more details.
13+
14+
You should have received a copy of the GNU Affero General Public License
15+
along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
using Newtonsoft.Json;
19+
20+
namespace Kick.API.Events
21+
{
22+
public class KicksGiftedEvent
23+
{
24+
[JsonProperty("message")]
25+
public string Message { get; internal set; }
26+
[JsonProperty("sender")]
27+
public KicksGiftingUser Sender { get; internal set; }
28+
[JsonProperty("gift")]
29+
public KicksGift Gift { get; internal set; }
30+
}
31+
32+
public class KicksGiftingUser
33+
{
34+
[JsonProperty("id")]
35+
public string Id { get; internal set; }
36+
[JsonProperty("username")]
37+
public string Username { get; internal set; }
38+
[JsonProperty("username_color")]
39+
public string Color { get; internal set; }
40+
}
41+
42+
public class KicksGift
43+
{
44+
[JsonProperty("gift_id")]
45+
public string GiftId { get; internal set; }
46+
[JsonProperty("name")]
47+
public string Name { get; internal set; }
48+
[JsonProperty("amount")]
49+
public int Amount { get; internal set; }
50+
[JsonProperty("type")]
51+
public string Type { get; internal set; }
52+
[JsonProperty("tier")]
53+
public string Tier { get; internal set; }
54+
[JsonProperty("character_limit")]
55+
public int CharacterLimit { get; internal set; }
56+
[JsonProperty("pinned_time")]
57+
public int PinnedTime { get; internal set; }
58+
}
59+
}

API/KickClient.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,7 @@ public async Task<Reward> UpdateReward(Channel channel, Reward reward)
613613
{ "should_redemptions_skip_request_queue", reward.ShouldRedemptionsSkipRequestQueue },
614614
{ "title", reward.Title },
615615
};
616-
if(reward.IsUserInputRequired)
616+
if(reward.IsUserInputRequired && !string.IsNullOrEmpty(reward.Prompt))
617617
data.Add("prompt", reward.Prompt);
618618
var response = await ApiJsonPatch<KickApiMessageOperationResponse<Reward>>($"/api/v2/channels/{channel.Slug}/rewards/{reward.Id}", data);
619619
if (response.Message != "OK")

API/KickEventListener.cs

Lines changed: 51 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ public sealed class KickEventListener
123123
public delegate void OnPredictionUpdatedHandler(Prediction prediction);
124124
public event OnPredictionUpdatedHandler OnPredictionUpdated;
125125

126+
public delegate void OnKicksGiftedHandler(KicksGiftedEvent kicksGiftedEvent);
127+
public event OnKicksGiftedHandler OnKicksGifted;
128+
126129
private readonly Pusher _pusherClient;
127130
//private delegate void PusherEventHandler(string eventType, PusherEvent eventData);
128131

@@ -155,6 +158,7 @@ public async Task JoinAsync(Models.Channel channel)
155158
private async Task RegisterChannel(Models.Channel channel)
156159
{
157160
var channels = new List<string> {
161+
$"channel_{channel.Id}",
158162
$"chatrooms.{channel.Chatroom.Id}.v2",
159163
$"predictions-channel-{channel.Id}"
160164
};
@@ -225,6 +229,12 @@ private void HandleEvent(string eventType, PusherEvent eventData)
225229

226230
switch (eventType)
227231
{
232+
// channel_<id>
233+
case "KicksGifted":
234+
var kicksGifted = JsonConvert.DeserializeObject<KicksGiftedEvent>(eventData.Data);
235+
OnKicksGifted?.Invoke(kicksGifted);
236+
return;
237+
228238
// chatrooms.<id>.v2
229239
case "App\\Events\\ChatMessageEvent":
230240
var chatMessageEvent = JsonConvert.DeserializeObject<ChatMessageEvent>(eventData.Data);
@@ -449,49 +459,50 @@ private void PollUpdate(Models.Channel channel, PollUpdateEvent pollUpdateEvent)
449459
if(!_currentPolls.ContainsKey(channel.Id))
450460
{
451461
_currentPolls[channel.Id] = pollUpdateEvent;
462+
463+
// Poll Created
464+
OnPollCreated?.Invoke(pollUpdateEvent);
465+
452466
var pollUpdater = new Thread(() =>
453467
{
454-
// Poll Created
455-
OnPollCreated?.Invoke(pollUpdateEvent);
456-
457468
// Wait for poll to end
458469
var waitHandle = new AutoResetEvent(false);
459-
var remaining = pollUpdateEvent.Poll.Remaining;
460-
var timer = new Timer(state => {
461-
if(!_currentPolls.TryGetValue(channel.Id, out var currentPoll))
462-
{
463-
// Not supposed to happen, thread will be stopped
464-
currentPoll = pollUpdateEvent; // Restore context
465-
pollUpdateEvent.State = PollState.Cancelled;
466-
} else
470+
if (pollUpdateEvent?.Poll != null)
471+
{
472+
var timer = new Timer(state =>
467473
{
468-
pollUpdateEvent = currentPoll; // Save context
469-
}
470-
471-
if(pollUpdateEvent.State == PollState.Cancelled)
472-
{
473-
OnPollCancelled?.Invoke(pollUpdateEvent);
474-
waitHandle.Set();
475-
return;
476-
}
477-
478-
currentPoll.Poll.Remaining = --remaining;
479-
if (currentPoll.Poll.Remaining >= 0)
480-
{
481-
OnPollUpdated?.Invoke(currentPoll);
482-
}
483-
else
484-
{
485-
currentPoll.Poll.Remaining = 0;
486-
currentPoll.State = PollState.Completed;
487-
OnPollCompleted?.Invoke(currentPoll);
488-
waitHandle.Set();
489-
}
490-
}, null, 1000, 1000);
491-
waitHandle.WaitOne();
492-
493-
// Stop Timer
494-
timer.Dispose();
474+
if (!_currentPolls.TryGetValue(channel.Id, out var currentPoll))
475+
{
476+
// Not supposed to happen, thread will be stopped
477+
currentPoll = pollUpdateEvent; // Restore context
478+
currentPoll.State = PollState.Cancelled;
479+
}
480+
481+
if (currentPoll.State == PollState.Cancelled)
482+
{
483+
OnPollCancelled?.Invoke(currentPoll);
484+
waitHandle.Set();
485+
return;
486+
}
487+
488+
--currentPoll.Poll.Remaining;
489+
if (currentPoll.Poll.Remaining >= 0)
490+
{
491+
OnPollUpdated?.Invoke(currentPoll);
492+
}
493+
else
494+
{
495+
currentPoll.Poll.Remaining = 0;
496+
currentPoll.State = PollState.Completed;
497+
OnPollCompleted?.Invoke(currentPoll);
498+
waitHandle.Set();
499+
}
500+
}, null, 1000, 1000);
501+
waitHandle.WaitOne();
502+
503+
// Stop Timer
504+
timer.Dispose();
505+
}
495506

496507
// Poll completed
497508
_currentPolls.Remove(channel.Id);
@@ -501,7 +512,8 @@ private void PollUpdate(Models.Channel channel, PollUpdateEvent pollUpdateEvent)
501512
else {
502513
if (pollUpdateEvent != null)
503514
{
504-
_currentPolls[channel.Id].Poll = pollUpdateEvent.Poll;
515+
if (pollUpdateEvent.Poll != null)
516+
_currentPolls[channel.Id].Poll.Options = pollUpdateEvent.Poll.Options;
505517
}
506518
else
507519
{

0 commit comments

Comments
 (0)