Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions thorn/Config/FeedConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ public class FeedConfig
public string CustomDescription { get; set; }
public bool NewPageAnnouncement { get; set; }
public uint EmbedColor { get; set; }
public int Delay { get; set; }
}
1 change: 1 addition & 0 deletions thorn/Config/config.example.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"token": "YOUR-TOKEN",
"rssProxy": "socks5://10.64.0.1:1080",
"helpText": {
"translate": "[Návod pro Překladatele](<http://scp-cs.wikidot.com/guide-for-translators>)\n[Rychlý Start pro Překladatele](<http://scp-cs.wikidot.com/translator-quickstart>)\n[Potřebné Překlady](<http://scp-cs.wikidot.com/translation-requests>)\n[Překladatelské Stránky](<http://scp-cs.wikidot.com/translators-pages>)",
"write": "[Jak napsat SCP](<http://scp-cs.wikidot.com/how-to-write-an-scp>)\n[Pravidla Kritizování](<http://scp-cs.wikidot.com/criticism-policy>)\n[Autorské Stránky](<http://scp-cs.wikidot.com/authors-pages>)",
Expand Down
15 changes: 10 additions & 5 deletions thorn/Config/feeds.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@
"Filter": ["nová stránka"],
"FilterIgnore": ["fragment:"],
"EmbedColor": 16711680,
"NewPageAnnouncement": true
"NewPageAnnouncement": true,
"Delay": 120
},

{
"Link": "http://scp-cs.wikidot.com/feed/site-changes.xml",
"ChannelIds": [710575651664429127],
"EmbedColor": 16711680,
"NewPageAnnouncement": false
"NewPageAnnouncement": false,
"Delay": 120
},
{
"Link": "http://o5command-cs.wikidot.com/feed/site-changes.xml",
Expand All @@ -25,21 +27,24 @@
"ChannelIds": [710575651664429127],
"Filter": ["nová stránka"],
"EmbedColor": 3329330,
"NewPageAnnouncement": false
"NewPageAnnouncement": false,
"Delay": 300
},
{
"Link": "http://scp-cs-sandbox-2.wikidot.com/feed/site-changes.xml",
"ChannelIds": [710575651664429127],
"Filter": ["nová stránka"],
"EmbedColor": 3329330,
"NewPageAnnouncement": false
"NewPageAnnouncement": false,
"Delay": 300
},
{
"Link": "http://scp-cs-sandbox-3.wikidot.com/feed/site-changes.xml",
"ChannelIds": [710575651664429127],
"Filter": ["nová stránka"],
"EmbedColor": 3329330,
"NewPageAnnouncement": false
"NewPageAnnouncement": false,
"Delay": 300
},
{
"Link": "http://scp-cs.wikidot.com/feed/forum/threads.xml",
Expand Down
90 changes: 68 additions & 22 deletions thorn/Jobs/RssJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,41 @@
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using CodeHollow.FeedReader;
using Discord;
using Discord.WebSocket;
using Html2Markdown;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Quartz;
using thorn.Config;

namespace thorn.Jobs;

public class RssJob : IJob
[DisallowConcurrentExecution] // there is shared state in this object
public partial class RssJob : IJob
{
public const int DefaultDelaySeconds = 10800;
public const string FeedIndexKey = "feedIndex";

private readonly ILogger<ReminderJob> _logger;
private readonly List<FeedConfig> _configs;
private readonly Dictionary<ulong, SocketTextChannel> _channels;
private Dictionary<FeedConfig, DateTime?> _lastUpdates;
private readonly HttpClient _httpClient;
private readonly Dictionary<FeedConfig, DateTime?> _lastUpdates;

public RssJob(ILogger<ReminderJob> logger, DiscordSocketClient client)
public RssJob(ILogger<ReminderJob> logger, DiscordSocketClient client, IConfiguration configuration)
{
_logger = logger;
_channels = new Dictionary<ulong, SocketTextChannel>();
_lastUpdates = new Dictionary<FeedConfig, DateTime?>();
_httpClient = BuildHttpClient(configuration["rssProxy"]);

_configs = JsonConvert.DeserializeObject<List<FeedConfig>>(File.ReadAllText("Config/feeds.json"));

Expand All @@ -43,28 +52,61 @@ public RssJob(ILogger<ReminderJob> logger, DiscordSocketClient client)
_lastUpdates.Add(feedConfig, null);
}

private HttpClient BuildHttpClient(string proxy)
{
if (string.IsNullOrWhiteSpace(proxy))
{
_logger.LogInformation("RSS proxy not configured");
return new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
}

_logger.LogInformation("RSS routed through proxy {Proxy}", proxy);
var handler = new SocketsHttpHandler
{
Proxy = new WebProxy(proxy),
UseProxy = true
};
return new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(10) };
}

public async Task Execute(IJobExecutionContext context)
{
foreach (var config in _configs)
var index = context.MergedJobDataMap.GetInt(FeedIndexKey);
if (index < 0 || index >= _configs.Count)
{
_logger.LogError("RssJob fired with out-of-range feed index {Index}", index);
return;
}

var config = _configs[index];
var newItems = await GetNewItems(config);
if (newItems is null) return;

foreach (var feedItem in newItems)
foreach (var channelId in config.ChannelIds)
{
var newItems = await GetNewItems(config);
if (newItems is null) continue;

foreach (var feedItem in newItems)
foreach (var channelId in config.ChannelIds)
{
var channel = _channels[channelId];
if (channel is null) continue;

await channel.SendMessageAsync(embed: GetEmbed(feedItem, config));
_logger.LogInformation("Sent RSS feed '{Title}' to #{Channel}", feedItem.Title, channel);
}
var channel = _channels[channelId];
if (channel is null) continue;

await channel.SendMessageAsync(embed: GetEmbed(feedItem, config));
_logger.LogInformation("Sent RSS feed '{Title}' to #{Channel}", feedItem.Title, channel);
}
}

private async Task<List<FeedItem>> GetNewItems(FeedConfig feedConfig)
{
var feed = await FeedReader.ReadAsync(feedConfig.Link);
Feed feed;
try
{
var content = await _httpClient.GetStringAsync(feedConfig.Link);
feed = FeedReader.ReadFromString(content);
}
catch (Exception e)
{
_logger.LogWarning(e, "Failed to fetch RSS feed {Link}", feedConfig.Link);
return null;
}

var lastUpdate = _lastUpdates[feedConfig];

if (lastUpdate is null)
Expand Down Expand Up @@ -122,7 +164,7 @@ private Embed GetEmbed(FeedItem feedItem, FeedConfig feedConfig)

private Embed GetAnnouncementEmbed(FeedItem feedItem, FeedConfig feedConfig, string text)
{
var title = Regex.Match(feedItem.Title, "\"(.*)\" - .*").Groups[1].Value;
var title = GetTitle(feedItem.Title);
var author = GetUsername(text);
var description = new StringBuilder("Nový článek na wiki! Yay! \\o/\n");

Expand All @@ -142,8 +184,12 @@ private Embed GetAnnouncementEmbed(FeedItem feedItem, FeedConfig feedConfig, str
}.Build();
}

private string GetUsername(string source)
{
return Regex.Match(source, "user:info\\/([^)]*)").Groups[1].Value;
}
private string GetTitle(string source) => TitleRegex().Match(source).Groups[1].Value;
private string GetUsername(string source) => UsernameRegex().Match(source).Groups[1].Value;

[GeneratedRegex("user:info\\/([^)]*)")]
private static partial Regex UsernameRegex();

[GeneratedRegex("\"(.*)\" - .*")]
private static partial Regex TitleRegex();
}
25 changes: 21 additions & 4 deletions thorn/Program.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Discord;
using Discord.Addons.Hosting;
using Discord.WebSocket;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Quartz;
using Serilog;
using Serilog.Events;
using thorn.Config;
using thorn.Jobs;
using thorn.Services;

Expand Down Expand Up @@ -68,10 +73,22 @@ private static async Task Main()

builder.Services.AddQuartz(configure =>
{
var tenMinSchedule = CronScheduleBuilder.CronSchedule("0 */10 * ? * *");
var feeds = JsonConvert.DeserializeObject<List<FeedConfig>>(File.ReadAllText("Config/feeds.json"));
var rssJob = new JobKey(nameof(RssJob));
configure.AddJob<RssJob>(rssJob)
.AddTrigger(t => t.ForJob(rssJob).WithSchedule(tenMinSchedule));
configure.AddJob<RssJob>(rssJob);

// create a trigger for each feed
for (var i = 0; i < feeds.Count; i++)
{
var index = i;
var delay = feeds[i].Delay > 0 ? feeds[i].Delay : RssJob.DefaultDelaySeconds;
var startAt = DateTimeOffset.UtcNow.AddSeconds(5 + index * 5);
configure.AddTrigger(t => t
.ForJob(rssJob)
.UsingJobData(RssJob.FeedIndexKey, index)
.StartAt(startAt)
.WithSimpleSchedule(s => s.WithIntervalInSeconds(delay).RepeatForever()));
}

var dailySchedule = CronScheduleBuilder.CronSchedule("0 0 0 * * ?");
var reminderJob = new JobKey(nameof(ReminderJob));
Expand Down
Loading