You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
356 lines
13 KiB
356 lines
13 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net.WebSockets;
|
|
using System.ServiceModel.Syndication;
|
|
using System.Threading.Tasks;
|
|
using System.Timers;
|
|
using System.Xml;
|
|
using Discord;
|
|
using Discord.WebSocket;
|
|
using Google.Apis.Auth.OAuth2;
|
|
using Google.Apis.Sheets.v4;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace DiscoBot.gsmeet
|
|
{
|
|
public class GSMeet : IModule
|
|
{
|
|
string IModule.Name { get => "GSMeet"; set => throw new NotImplementedException(); }
|
|
private SocketGuild guild;
|
|
private GSMeetContext gsmeetContext;
|
|
private Dictionary<string, Timer> timers = new Dictionary<string, Timer>();
|
|
private List<WebSocket> webSockets = new List<WebSocket>();
|
|
|
|
public Dictionary<string, Func<SocketMessage, string[], Task>> Commands { get; set; } = new Dictionary<string, Func<SocketMessage, string[], Task>>();
|
|
|
|
private static readonly string[] Scopes = { SheetsService.Scope.Spreadsheets };
|
|
private static readonly string ApplicationName = "DiscoBot";
|
|
private SheetsService service;
|
|
|
|
|
|
|
|
public GSMeet(SocketGuild guild)
|
|
{
|
|
this.guild = guild;
|
|
gsmeetContext = new GSMeetContext(guild.Id);
|
|
gsmeetContext.Database.EnsureCreated();
|
|
|
|
GoogleCredential credential;
|
|
using(var stream = new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
|
|
{
|
|
credential = GoogleCredential.FromStream(stream).CreateScoped(Scopes);
|
|
}
|
|
service = new SheetsService(new Google.Apis.Services.BaseClientService.Initializer()
|
|
{
|
|
HttpClientInitializer = credential,
|
|
ApplicationName = ApplicationName
|
|
});
|
|
|
|
gsmeetContext.GSheets.Include(s => s.NotifiedEvents).Load();
|
|
gsmeetContext.GSheets.Include(s => s.NotifiedUsers).Load();
|
|
|
|
foreach (var f in gsmeetContext.GSheets)
|
|
{
|
|
if(f.NotifiedUsers == null)
|
|
{
|
|
f.NotifiedUsers = new List<GSMeetUserNotification>();
|
|
}
|
|
if(f.NotifiedEvents == null)
|
|
{
|
|
f.NotifiedEvents = new List<GSMeetEventNotification>();
|
|
}
|
|
InitializeSheet(f);
|
|
HandleSheetCheck(f);
|
|
}
|
|
|
|
Commands.Add("gsmeetadd", HandleGSMeetAddCommand);
|
|
Commands.Add("gsmeetdel", HandleGSMeetDelCommand);
|
|
Commands.Add("gsmeetlist", HandleGSMeetListCommand);
|
|
Commands.Add("gsmeetdebug", HandleGSMeetDebugCommand);
|
|
}
|
|
|
|
private Task InitializeSheet(DBSheet sheet)
|
|
{
|
|
Console.WriteLine("Found sheet " + sheet.Name);
|
|
Timer timer = new Timer(sheet.CheckInterval.TotalMilliseconds);
|
|
timer.AutoReset = true;
|
|
timer.Elapsed += async (sender, e) =>
|
|
{
|
|
await Task.Run(() => HandleSheetCheck(sheet));
|
|
};
|
|
timer.Start();
|
|
timers.Add(sheet.Name, timer);
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task DeinitializeSheets(DBSheet sheet)
|
|
{
|
|
/*
|
|
Timer t = timers[feed.Name];
|
|
t.Stop();
|
|
timers.Remove(feed.Name);
|
|
*/
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task ParseGSMeetSheet(GSMeetSheetData sheetData, IList<IList<object>> values)
|
|
{
|
|
var daterow = values[1];
|
|
var time = TimeSpan.Parse((string)daterow[0]);
|
|
var events = new List<DateTimeOffset>();
|
|
var users = new List<GSMeetUser>();
|
|
for (int i = 2; i < daterow.Count; ++i)
|
|
{
|
|
var col = daterow[i];
|
|
var d = DateTimeOffset.Parse((string)col, null, DateTimeStyles.AssumeUniversal);
|
|
events.Add(d + time);
|
|
}
|
|
for (int i = 2; i < values.Count; ++i)
|
|
{
|
|
var row = values[i];
|
|
GSMeetUser u = new GSMeetUser();
|
|
u.DiscordTag = (string)row[0];
|
|
u.Name = (string)row[1];
|
|
for (int j = 2; j < row.Count; ++j)
|
|
{
|
|
var str = (string)row[j];
|
|
if(str != "")
|
|
u.Signups.Add(events[j - 2], str);
|
|
}
|
|
users.Add(u);
|
|
}
|
|
sheetData.Dates = events;
|
|
sheetData.Users = users;
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private async Task SignupNotify(SocketTextChannel chan, GSMeetSheetData sheetData)
|
|
{
|
|
foreach(var date in sheetData.Dates)
|
|
{
|
|
|
|
if(DateTimeOffset.UtcNow + new TimeSpan(4,0,0,0) > date && date > DateTimeOffset.UtcNow)
|
|
{
|
|
await chan.SendMessageAsync("I should signup notify for " + date);
|
|
foreach (var user in sheetData.Users)
|
|
{
|
|
if (!user.Signups.ContainsKey(date))
|
|
{
|
|
bool doNotify = true;
|
|
foreach(var notification in sheetData.Sheet.NotifiedUsers)
|
|
{
|
|
if(notification.Date == date && notification.DiscordTag == user.DiscordTag)
|
|
{
|
|
Console.WriteLine("User already notified.");
|
|
doNotify = false;
|
|
break;
|
|
}
|
|
}
|
|
if (doNotify)
|
|
{
|
|
await chan.SendMessageAsync("Hey " + user.DiscordTag + " please sign up for our raid on " + date);
|
|
var n = new GSMeetUserNotification(user.DiscordTag, date);
|
|
sheetData.Sheet.NotifiedUsers.Add(n);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task RaidNotify(SocketTextChannel chan, GSMeetSheetData sheetData)
|
|
{
|
|
foreach (var date in sheetData.Dates)
|
|
{
|
|
|
|
if (DateTimeOffset.UtcNow + new TimeSpan(2,0,0,0) > date)
|
|
{
|
|
await chan.SendMessageAsync("I should raid notify for " + date);
|
|
//
|
|
}
|
|
}
|
|
}
|
|
|
|
private Task CleanupNotifyDB(GSMeetSheetData sheetData)
|
|
{
|
|
/*
|
|
foreach(var n in sheetData.Sheet.NotifiedEvents)
|
|
{
|
|
if(DateTimeOffset.UtcNow > n.Date)
|
|
{
|
|
sheetData.Sheet.NotifiedEvents.Remove(n);
|
|
}
|
|
}
|
|
foreach (var n in sheetData.Sheet.NotifiedUsers)
|
|
{
|
|
if (DateTimeOffset.UtcNow > n.Date)
|
|
{
|
|
sheetData.Sheet.NotifiedUsers.Remove(n);
|
|
}
|
|
}
|
|
gsmeetContext.SaveChanges();
|
|
*/
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
|
|
private async Task HandleSheetCheck(DBSheet sheet)
|
|
{
|
|
var sheetData = new GSMeetSheetData(sheet);
|
|
SocketTextChannel c = guild.Channels.Where(g => g.Id == sheet.Channel).Single() as SocketTextChannel;
|
|
await c.SendMessageAsync("Checking sheet " + sheet.Id + " :3");
|
|
var values = FetchRangeFromSheet(sheet.Id, $"{sheet.SheetName}!A1:I10");
|
|
if (values == null || values.Count < 1)
|
|
{
|
|
await c.SendMessageAsync("No values found.");
|
|
}
|
|
await ParseGSMeetSheet(sheetData, values);
|
|
await c.SendMessageAsync("Parsed sheet " + sheet.Id);
|
|
await SignupNotify(c, sheetData);
|
|
await RaidNotify(c, sheetData);
|
|
gsmeetContext.SaveChanges();
|
|
CleanupNotifyDB(sheetData);
|
|
/*
|
|
var str = "";
|
|
foreach (var row in values)
|
|
{
|
|
foreach (var col in row)
|
|
{
|
|
str += col + "| ";
|
|
}
|
|
str += "\n";
|
|
}
|
|
c.SendMessageAsync("Result: " + str);
|
|
*/
|
|
}
|
|
|
|
private Task HandleGSMeetAddCommand(SocketMessage msg, string[] parameters)
|
|
{
|
|
var gchan = msg.Channel as IGuildChannel;
|
|
string name = parameters[1];
|
|
string sheetId = parameters[2];
|
|
string sheetName = parameters[3];
|
|
try
|
|
{
|
|
int timeSec = Int32.Parse(parameters[4]);
|
|
DBSheet sheet = new DBSheet();
|
|
sheet.Channel = gchan.Id;
|
|
sheet.Name = name;
|
|
sheet.Id = sheetId;
|
|
sheet.SheetName = sheetName;
|
|
sheet.LastChecked = DateTimeOffset.Now;
|
|
sheet.CheckInterval = new TimeSpan(0, 0, timeSec);
|
|
gsmeetContext.GSheets.Add(sheet);
|
|
try
|
|
{
|
|
gsmeetContext.SaveChanges();
|
|
InitializeSheet(sheet);
|
|
msg.Channel.SendMessageAsync("Sheet " + sheet.Name + " with id " + sheet.Id + " saved.");
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
msg.Channel.SendMessageAsync("Unable to save sheet feed.");
|
|
}
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
msg.Channel.SendMessageAsync("Unable to save sheet. Invalid check time.");
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task HandleGSMeetListCommand(SocketMessage msg, string[] parameters)
|
|
{
|
|
/*
|
|
List<RssFeed> feeds = rssContext.RssFeeds.Where(f => f.Channel == msg.Channel.Id).ToList();
|
|
List<string> m = new List<string>();
|
|
m.Add("Feeds for this channel are:");
|
|
m.Add("Name | URL | CheckInterval | LastChecked");
|
|
msg.Channel.SendMessageAsync(string.Join("\n", m));
|
|
m = new List<string>();
|
|
int cnt = 0;
|
|
foreach (var f in feeds)
|
|
{
|
|
m.Add("- " + f.Name + " | " + f.Url + " | " + f.CheckInterval + " | " + f.LastChecked);
|
|
cnt++;
|
|
if(cnt > 4)
|
|
{
|
|
cnt = 0;
|
|
msg.Channel.SendMessageAsync(string.Join("\n", m));
|
|
}
|
|
}
|
|
if(cnt > 0)
|
|
msg.Channel.SendMessageAsync(string.Join("\n", m));
|
|
*/
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task HandleGSMeetDelCommand(SocketMessage msg, string[] parameters)
|
|
{
|
|
/*
|
|
var gchan = msg.Channel as IGuildChannel;
|
|
string name = parameters[1];
|
|
try
|
|
{
|
|
RssFeed f = rssContext.RssFeeds.Where(f => f.Name == name).Single();
|
|
rssContext.Remove(f);
|
|
rssContext.SaveChanges();
|
|
DeinitializeFeed(f);
|
|
msg.Channel.SendMessageAsync("Removed feed " + f.Name);
|
|
} catch(InvalidOperationException)
|
|
{
|
|
msg.Channel.SendMessageAsync("Could not find feed " + name);
|
|
}
|
|
*/
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private Task HandleGSMeetDebugCommand(SocketMessage msg, string[] parameters)
|
|
{
|
|
var gchan = msg.Channel as IGuildChannel;
|
|
string name = parameters[1];
|
|
string sheetId = parameters[2];
|
|
string sheetName = parameters[3];
|
|
|
|
var values = FetchRangeFromSheet(sheetId, $"{sheetName}!A1:I10");
|
|
if(values == null || values.Count < 1)
|
|
{
|
|
msg.Channel.SendMessageAsync("No values found.");
|
|
return Task.CompletedTask;
|
|
}
|
|
var str = "";
|
|
foreach(var row in values)
|
|
{
|
|
foreach(var col in row)
|
|
{
|
|
str += col + "| ";
|
|
}
|
|
str += "\n";
|
|
}
|
|
msg.Channel.SendMessageAsync("Result: "+str);
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private IList<IList<object>> FetchRangeFromSheet(string sheetId, string range)
|
|
{
|
|
var request = service.Spreadsheets.Values.Get(sheetId, range);
|
|
var response = request.Execute();
|
|
return response.Values;
|
|
}
|
|
|
|
public void Initialize()
|
|
{
|
|
Console.WriteLine("Initializing gsmeet...");
|
|
}
|
|
|
|
public async Task OnNewWebSocketAsync(WebSocket ws, TaskCompletionSource<object> tcs)
|
|
{
|
|
Console.WriteLine("Calendar " + guild.Id + " has a new websocket.");
|
|
webSockets.Add(ws);
|
|
}
|
|
}
|
|
}
|
|
|