First try of actually working code

This commit is contained in:
amki 2019-10-17 04:23:13 +02:00
parent 7479534a9f
commit 6885d6b6b9
12 changed files with 203 additions and 167 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

Binary file not shown.

View File

@ -4,7 +4,9 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Security.Cryptography;
using System.ServiceModel.Syndication; using System.ServiceModel.Syndication;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Timers; using System.Timers;
using System.Xml; using System.Xml;
@ -30,6 +32,8 @@ namespace DiscoBot.gsmeet
private static readonly string ApplicationName = "DiscoBot"; private static readonly string ApplicationName = "DiscoBot";
private SheetsService service; private SheetsService service;
private Dictionary<string, GSMeetingTimers> meetingTimers = new Dictionary<string, GSMeetingTimers>();
public GSMeet(SocketGuild guild) public GSMeet(SocketGuild guild)
@ -49,21 +53,11 @@ namespace DiscoBot.gsmeet
ApplicationName = ApplicationName ApplicationName = ApplicationName
}); });
gsmeetContext.GSheets.Include(s => s.NotifiedEvents).Load();
gsmeetContext.GSheets.Include(s => s.NotifiedUsers).Load();
foreach (var f in gsmeetContext.GSheets) foreach (var f in gsmeetContext.GSheets)
{ {
if(f.NotifiedUsers == null) var gssheet = new GSSheet(f);
{ InitializeSheet(gssheet);
f.NotifiedUsers = new List<GSMeetUserNotification>(); HandleSheetCheck(gssheet);
}
if(f.NotifiedEvents == null)
{
f.NotifiedEvents = new List<GSMeetEventNotification>();
}
InitializeSheet(f);
HandleSheetCheck(f);
} }
Commands.Add("gsmeetadd", HandleGSMeetAddCommand); Commands.Add("gsmeetadd", HandleGSMeetAddCommand);
@ -72,21 +66,21 @@ namespace DiscoBot.gsmeet
Commands.Add("gsmeetdebug", HandleGSMeetDebugCommand); Commands.Add("gsmeetdebug", HandleGSMeetDebugCommand);
} }
private Task InitializeSheet(DBSheet sheet) private Task InitializeSheet(GSSheet sheet)
{ {
Console.WriteLine("Found sheet " + sheet.Name); Console.WriteLine("Found sheet " + sheet.Db.Name);
Timer timer = new Timer(sheet.CheckInterval.TotalMilliseconds); Timer timer = new Timer(sheet.Db.CheckInterval.TotalMilliseconds);
timer.AutoReset = true; timer.AutoReset = true;
timer.Elapsed += async (sender, e) => timer.Elapsed += async (sender, e) =>
{ {
await Task.Run(() => HandleSheetCheck(sheet)); await Task.Run(() => HandleSheetCheck(sheet));
}; };
timer.Start(); timer.Start();
timers.Add(sheet.Name, timer); timers.Add(sheet.Db.Name, timer);
return Task.CompletedTask; return Task.CompletedTask;
} }
private Task DeinitializeSheets(DBSheet sheet) private Task DeinitializeSheets(GSSheet sheet)
{ {
/* /*
Timer t = timers[feed.Name]; Timer t = timers[feed.Name];
@ -96,125 +90,103 @@ namespace DiscoBot.gsmeet
return Task.CompletedTask; return Task.CompletedTask;
} }
private Task ParseGSMeetSheet(GSMeetSheetData sheetData, IList<IList<object>> values) private GSMeeting ParseMeeting(SocketTextChannel c, IList<IList<object>> values)
{ {
var daterow = values[1]; var meeting = new GSMeeting();
var time = TimeSpan.Parse((string)daterow[0]); var dateRow = values[1];
var events = new List<DateTimeOffset>(); var time = TimeSpan.Parse((string)dateRow[0]);
var users = new List<GSMeetUser>(); var idString = "";
for (int i = 2; i < daterow.Count; ++i) for (int i = 2; i < dateRow.Count; ++i)
{ {
var col = daterow[i]; var col = dateRow[i];
var d = DateTimeOffset.Parse((string)col, null, DateTimeStyles.AssumeUniversal); var d = DateTimeOffset.Parse((string)col, null, DateTimeStyles.AssumeUniversal);
events.Add(d + time); var dateTime = d + time;
idString += dateTime.ToString("o");
meeting.Dates.Add(dateTime);
} }
for (int i = 2; i < values.Count; ++i) for(int i=2;i<values.Count;++i)
{ {
var row = values[i]; var row = values[i];
GSMeetUser u = new GSMeetUser(); var u = new GSMeetingUser();
u.DiscordTag = (string)row[0]; u.DiscordTag = (string)row[0];
try {
u.User = guild.Users.Where(gu => (gu.Username + "#" + gu.DiscriminatorValue) == u.DiscordTag).Single();
} catch(InvalidOperationException e)
{
// Too spammy, do something about it? Don't rely on u.User
//c.SendMessageAsync("Could not find User "+ u.DiscordTag+" in Discord, cannot mention.");
}
u.Name = (string)row[1]; u.Name = (string)row[1];
for (int j = 2; j < row.Count; ++j) for (int j = 2; j < row.Count; ++j)
{ {
var str = (string)row[j]; var str = (string)row[j];
if(str != "") u.Signups.Add(str);
u.Signups.Add(events[j - 2], str);
} }
users.Add(u); // Fill the empty columns that the api skipped
while(u.Signups.Count < meeting.Dates.Count)
{
u.Signups.Add("");
}
meeting.Users.Add(u);
} }
sheetData.Dates = events; var sha1 = new SHA1CryptoServiceProvider();
sheetData.Users = users; var binHash = sha1.ComputeHash(Encoding.ASCII.GetBytes(idString));
return Task.CompletedTask; var hash = BitConverter.ToString(binHash).Replace("-", string.Empty);
meeting.Id = hash;
return meeting;
} }
private async Task SignupNotify(SocketTextChannel chan, GSMeetSheetData sheetData) private async Task<List<GSMeeting>> ParseGSMeetSheet(GSSheet sheet, IList<IList<object>> values)
{ {
foreach(var date in sheetData.Dates) SocketTextChannel c = guild.Channels.Where(g => g.Id == sheet.Db.Channel).Single() as SocketTextChannel;
int meetStart = -1;
var val = values as List<IList<object>>;
var meetings = new List<GSMeeting>();
for (var i=0;i<values.Count;++i)
{ {
var row = values[i] as List<object>;
if(DateTimeOffset.UtcNow + new TimeSpan(4,0,0,0) > date && date > DateTimeOffset.UtcNow) if(row.Count < 1)
{ {
await chan.SendMessageAsync("I should signup notify for " + date); continue;
foreach (var user in sheetData.Users) }
if((string)row[0] == "Raid")
{
if(meetStart == -1)
{ {
if (!user.Signups.ContainsKey(date)) meetStart = i;
{ } else
bool doNotify = true; {
foreach(var notification in sheetData.Sheet.NotifiedUsers) meetings.Add(ParseMeeting(c, val.GetRange(meetStart,(i-meetStart-2))));
{ meetStart = i;
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);
}
}
} }
} }
} }
// Last meeting starts with Raid, thus sets meetStart but no other meeting finishes it, so parse last rows as last meeting
meetings.Add(ParseMeeting(c, val.GetRange(meetStart, (values.Count - meetStart))));
Console.WriteLine("I parsed " + meetings.Count + " meetings from the sheet.");
return meetings;
} }
private async Task RaidNotify(SocketTextChannel chan, GSMeetSheetData sheetData) private async Task HandleSheetCheck(GSSheet sheet)
{ {
foreach (var date in sheetData.Dates) SocketTextChannel c = guild.Channels.Where(g => g.Id == sheet.Db.Channel).Single() as SocketTextChannel;
{ Console.WriteLine("Checking sheet " + sheet.Db.Id + " :3");
var values = FetchRangeFromSheet(sheet.Db.Id, $"{sheet.Db.SheetName}!A:I");
if (DateTimeOffset.UtcNow + new TimeSpan(2,0,0,0) > date)
{
await chan.SendMessageAsync("I should raid notify for " + date);
//
}
}
}
private Task CleanupNotifyDB(GSMeetSheetData sheetData)
{
for(var i=sheetData.Sheet.NotifiedEvents.Count-1;i>=0;i--)
{
var n = sheetData.Sheet.NotifiedEvents[i];
if(DateTimeOffset.UtcNow > n.Date)
{
sheetData.Sheet.NotifiedEvents.Remove(n);
gsmeetContext.Entry(n).State = EntityState.Deleted;
}
}
for (var i = sheetData.Sheet.NotifiedUsers.Count - 1; i >= 0; i--)
{
var n = sheetData.Sheet.NotifiedUsers[i];
if (DateTimeOffset.UtcNow > n.Date)
{
sheetData.Sheet.NotifiedUsers.Remove(n);
gsmeetContext.Entry(n).State = EntityState.Deleted;
}
}
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) if (values == null || values.Count < 1)
{ {
await c.SendMessageAsync("No values found."); Console.WriteLine("No values found.");
} }
await ParseGSMeetSheet(sheetData, values); var meetings = await ParseGSMeetSheet(sheet, values);
await c.SendMessageAsync("Parsed sheet " + sheet.Id); foreach(var meeting in meetings)
await SignupNotify(c, sheetData); {
await RaidNotify(c, sheetData); if(!sheet.Timers.ContainsKey(meeting.Id))
gsmeetContext.SaveChanges(); {
CleanupNotifyDB(sheetData); sheet.Timers.Add(meeting.Id, new GSMeetingTimers());
}
sheet.Timers[meeting.Id].UpdateTimers(c, meeting);
}
Console.WriteLine("Parsed sheet " + sheet.Db.Id);
/* /*
var str = ""; var str = "";
foreach (var row in values) foreach (var row in values)
@ -249,7 +221,8 @@ namespace DiscoBot.gsmeet
try try
{ {
gsmeetContext.SaveChanges(); gsmeetContext.SaveChanges();
InitializeSheet(sheet); var gssheet = new GSSheet(sheet);
InitializeSheet(gssheet);
msg.Channel.SendMessageAsync("Sheet " + sheet.Name + " with id " + sheet.Id + " saved."); msg.Channel.SendMessageAsync("Sheet " + sheet.Name + " with id " + sheet.Id + " saved.");
} }
catch (InvalidOperationException) catch (InvalidOperationException)
@ -313,27 +286,7 @@ namespace DiscoBot.gsmeet
private Task HandleGSMeetDebugCommand(SocketMessage msg, string[] parameters) private Task HandleGSMeetDebugCommand(SocketMessage msg, string[] parameters)
{ {
var gchan = msg.Channel as IGuildChannel; var gchan = msg.Channel as IGuildChannel;
string name = parameters[1]; msg.Channel.SendMessageAsync("Username: "+msg.Author.Username+"#"+msg.Author.DiscriminatorValue);
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; return Task.CompletedTask;
} }

View File

@ -6,6 +6,7 @@ using System.Timers;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using Discord.WebSocket;
namespace DiscoBot.gsmeet namespace DiscoBot.gsmeet
{ {
@ -34,53 +35,40 @@ namespace DiscoBot.gsmeet
public ulong Channel { get; set; } public ulong Channel { get; set; }
public TimeSpan CheckInterval { get; set; } public TimeSpan CheckInterval { get; set; }
public DateTimeOffset LastChecked { get; set; } public DateTimeOffset LastChecked { get; set; }
public List<GSMeetEventNotification> NotifiedEvents { get; set; }
public List<GSMeetUserNotification> NotifiedUsers { get; set; }
} }
public class GSMeetEventNotification public class GSSheet
{ {
[ScaffoldColumn(false)] public GSSheet(DBSheet db)
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public long Id { get; set; }
public DateTimeOffset Date { get; set; }
}
public class GSMeetUserNotification
{
public GSMeetUserNotification(string discordTag, DateTimeOffset date)
{ {
this.DiscordTag = discordTag; this.Db = db;
this.Date = date; this.Timers = new Dictionary<string, GSMeetingTimers>();
}
public DBSheet Db { get; set; }
public Dictionary<string, GSMeetingTimers> Timers { get; set; }
}
public class GSMeetingUser
{
public GSMeetingUser()
{
this.Signups = new List<string>();
} }
[ScaffoldColumn(false)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public long Id { get; set; }
public string DiscordTag { get; set; } public string DiscordTag { get; set; }
public DateTimeOffset Date { get; set; } public SocketGuildUser User { get; set; }
public string Name { get; set; }
public List<string> Signups { get; set; }
} }
public class GSMeetSheetData public class GSMeeting
{ {
public GSMeetSheetData(DBSheet sheet) public GSMeeting()
{ {
this.Sheet = sheet; this.Users = new List<GSMeetingUser>();
this.Dates = new List<DateTimeOffset>();
} }
public DBSheet Sheet { get; set; } public string Id { get; set; }
public List<GSMeetUser> Users { get; set; } public List<GSMeetingUser> Users { get; set; }
public List<DateTimeOffset> Dates { get; set; } public List<DateTimeOffset> Dates { get; set; }
} }
public class GSMeetUser
{
public GSMeetUser()
{
this.Signups = new Dictionary<DateTimeOffset, string>();
}
public string DiscordTag { get; set; }
public string Name { get; set; }
public Dictionary<DateTimeOffset, string> Signups { get; set; }
}
} }

View File

@ -0,0 +1,95 @@
using Discord.WebSocket;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
namespace DiscoBot.gsmeet
{
public class GSMeetingTimers
{
private List<TimeSpan> WarningTimes = new List<TimeSpan> {
new TimeSpan(2, 0, 0, 0),
new TimeSpan(1, 0, 0, 0) };
private List<TimeSpan> UserTimes = new List<TimeSpan> {
new TimeSpan(7, 0, 0, 0),
new TimeSpan(6, 0, 0, 0),
new TimeSpan(5, 0, 0, 0),
new TimeSpan(4, 0, 0, 0),
new TimeSpan(3, 0, 0, 0) };
private Dictionary<DateTimeOffset, GSEventTimers> events = new Dictionary<DateTimeOffset, GSEventTimers>();
public GSMeetingTimers()
{
}
public void UpdateTimers(SocketTextChannel chan, GSMeeting meeting)
{
for(var i=0;i<meeting.Dates.Count;++i)
{
var date = meeting.Dates[i];
// Keep this so we can use it to pick from user.Signups later! (async)
var idx = i;
GSEventTimers evt;
if(events.ContainsKey(date))
{
evt = events[date];
} else
{
evt = new GSEventTimers();
events.Add(date, evt);
}
foreach(var t in evt.UserTimers)
{
t.Dispose();
}
evt.UserTimers = new List<Timer>();
foreach(var ts in UserTimes)
{
var when = date - ts;
if(when < DateTimeOffset.Now)
{
continue;
}
Console.WriteLine("Setting a timer for " + when + " for evt " + date);
var whents = when - DateTimeOffset.Now;
// Debug resolve timers after 30s
//Timer t = new Timer(new TimeSpan(0, 0, 30).TotalMilliseconds);
Timer t = new Timer(whents.TotalMilliseconds);
t.AutoReset = false;
var users = meeting.Users;
t.Elapsed += async (sender, e) =>
{
foreach(var user in users)
{
if(user.Signups[idx] != "1" && user.Signups[idx] != "0" && user.Signups[idx] != "0.5")
{
if(user.User != null) {
await chan.SendMessageAsync("Hey " + user.User.Mention + " could you please sign up for our raid on " + date + "?");
} else
{
await chan.SendMessageAsync("Hey " + user.Name + " could you please sign up for our raid on " + date + "?");
}
}
}
};
t.Start();
evt.UserTimers.Add(t);
}
}
}
}
public class GSEventTimers
{
public GSEventTimers()
{
this.UserTimers = new List<Timer>();
this.WarningTimers = new List<Timer>();
}
public List<Timer> UserTimers { get; set; }
public List<Timer> WarningTimers { get; set; }
}
}