Announcement runner rewrite (#114)

Full rewrite of the announcement runner in kotlin and remove several now-unused deprecated methods
This commit is contained in:
Nova Fox
2021-10-02 23:01:01 -05:00
committed by GitHub
parent 5769255720
commit fd1602bc4f
17 changed files with 288 additions and 761 deletions

View File

@@ -1,13 +0,0 @@
root = true
[*]
indent_style = space
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
# Credit to Discord4J for the .editorconfig file <3

View File

@@ -26,7 +26,7 @@ val discord4jVersion = "3.2.0" //Has to be here to show up in git properties tas
allprojects {
//Project props
group = "org.dreamexposure.discal"
version = "4.1.2"
version = "4.1.3-SNAPSHOT"
description = "DisCal"
//Plugins

View File

@@ -2,28 +2,23 @@ package org.dreamexposure.discal.client.message;
import com.google.api.services.calendar.model.Event;
import discord4j.common.util.Snowflake;
import discord4j.core.object.entity.*;
import discord4j.core.object.entity.Guild;
import discord4j.core.object.entity.Member;
import discord4j.core.object.entity.Role;
import discord4j.core.object.entity.channel.GuildChannel;
import discord4j.core.object.entity.channel.GuildMessageChannel;
import discord4j.core.spec.EmbedCreateSpec;
import discord4j.rest.http.client.ClientException;
import discord4j.rest.util.Image;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.dreamexposure.discal.client.DisCalClient;
import org.dreamexposure.discal.core.database.DatabaseManager;
import org.dreamexposure.discal.core.enums.announcement.AnnouncementStyle;
import org.dreamexposure.discal.core.enums.announcement.AnnouncementType;
import org.dreamexposure.discal.core.enums.event.EventColor;
import org.dreamexposure.discal.core.object.BotSettings;
import org.dreamexposure.discal.core.object.GuildSettings;
import org.dreamexposure.discal.core.object.announcement.Announcement;
import org.dreamexposure.discal.core.object.calendar.CalendarData;
import org.dreamexposure.discal.core.object.event.EventData;
import org.dreamexposure.discal.core.utils.GlobalVal;
import org.dreamexposure.discal.core.utils.ImageUtils;
import org.dreamexposure.discal.core.utils.RoleUtils;
import org.dreamexposure.discal.core.utils.UserUtils;
import org.dreamexposure.discal.core.wrapper.google.CalendarWrapper;
import org.dreamexposure.discal.core.wrapper.google.EventWrapper;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -68,7 +63,7 @@ public class AnnouncementMessageFormatter {
embed.author("DisCal", BotSettings.BASE_URL.get(), GlobalVal.getIconUrl());
embed.title(Messages.getMessage("Embed.Announcement.Info.Title", settings));
embed.addField(Messages.getMessage("Embed.Announcement.Info.ID", settings), a.getAnnouncementId().toString(), true);
embed.addField(Messages.getMessage("Embed.Announcement.Info.ID", settings), a.getId().toString(), true);
embed.addField(Messages.getMessage("Embed.Announcement.Info.Type", settings), a.getType().name(), true);
@@ -135,7 +130,7 @@ public class AnnouncementMessageFormatter {
embed.author("DisCal", BotSettings.BASE_URL.get(), GlobalVal.getIconUrl());
embed.title(Messages.getMessage("Embed.Announcement.Condensed.Title", settings));
embed.addField(Messages.getMessage("Embed.Announcement.Condensed.ID", settings), a.getAnnouncementId().toString(), false);
embed.addField(Messages.getMessage("Embed.Announcement.Condensed.ID", settings), a.getId().toString(), false);
embed.addField(Messages.getMessage("Embed.Announcement.Condensed.Time", settings), condensedTime(a), false);
if (a.getType().equals(AnnouncementType.SPECIFIC)) {
@@ -172,238 +167,6 @@ public class AnnouncementMessageFormatter {
}));
}
public static Mono<EmbedCreateSpec> getCondensedAnnouncementEmbed(Announcement a, int calNum,
GuildSettings settings) {
Mono<Guild> guild = DisCalClient.getClient().getGuildById(settings.getGuildID());
Mono<Event> event = Mono.just(a)
.map(Announcement::getType)
.filter(t -> t.equals(AnnouncementType.SPECIFIC))
.flatMap(t -> DatabaseManager.INSTANCE.getCalendar(a.getGuildId(), calNum))
.flatMap(cd -> EventWrapper.INSTANCE.getEvent(cd, a.getEventId()))
.defaultIfEmpty(new Event());
Mono<EventData> eData = Mono.just(a)
.map(Announcement::getType)
.filter(t -> t.equals(AnnouncementType.SPECIFIC) || t.equals(AnnouncementType.RECUR))
.flatMap(t -> DatabaseManager.INSTANCE.getEventData(a.getGuildId(), a.getEventId()))
.defaultIfEmpty(new EventData()).cache();
Mono<Boolean> img = eData.filter(EventData::shouldBeSaved)
.flatMap(ed -> ImageUtils.validate(ed.getImageLink(), settings.getPatronGuild()))
.defaultIfEmpty(false);
return Mono.zip(guild, event, eData, img)
.map(TupleUtils.function((g, e, ed, hasImg) -> {
var embed = EmbedCreateSpec.builder();
if (settings.getBranded())
embed.author(g.getName(), BotSettings.BASE_URL.get(),
g.getIconUrl(Image.Format.PNG).orElse(GlobalVal.getIconUrl()));
else
embed.author("DisCal", BotSettings.BASE_URL.get(), GlobalVal.getIconUrl());
embed.title(Messages.getMessage("Embed.Announcement.Condensed.Title", settings));
embed.addField(Messages.getMessage("Embed.Announcement.Condensed.ID", settings), a.getAnnouncementId().toString(), false);
embed.addField(Messages.getMessage("Embed.Announcement.Condensed.Time", settings), condensedTime(a), false);
if (a.getType().equals(AnnouncementType.SPECIFIC)) {
embed.addField(Messages.getMessage("Embed.Announcement.Condensed.EventID", settings), a.getEventId(), false);
if (hasImg)
embed.thumbnail(ed.getImageLink());
if (e.getSummary() != null) {
String summary = e.getSummary();
if (summary.length() > 250) {
summary = summary.substring(0, 250);
summary = summary + " (continues on Google Calendar View)";
}
embed.addField(Messages.getMessage("Embed.Announcement.Condensed.Summary", settings), summary, true);
}
} else if (a.getType().equals(AnnouncementType.COLOR)) {
embed.addField(Messages.getMessage("Embed.Announcement.Condensed.Color", settings), a.getEventColor().name(), true);
} else if (a.getType().equals(AnnouncementType.RECUR)) {
embed.addField(Messages.getMessage("Embed.Announcement.Condensed.RecurID", settings), a.getEventId(), true);
}
embed.footer(Messages.getMessage("Embed.Announcement.Condensed.Type", "%type%", a.getType().name(),
settings), null);
if (a.getType().equals(AnnouncementType.COLOR)) {
embed.color(a.getEventColor().asColor());
} else {
embed.color(GlobalVal.getDiscalColor());
}
embed.addField(Messages.getMessage("Embed.Announcement.Info.Enabled", settings), a.getEnabled() + "", true);
return embed.build();
}));
}
private static Mono<EmbedCreateSpec> getRealAnnouncementEmbed(Announcement a, Event event, CalendarData cd,
GuildSettings settings) {
Mono<Guild> guild = DisCalClient.getClient().getGuildById(settings.getGuildID());
Mono<String> startDate = EventMessageFormatter
.getHumanReadableDate(event.getStart(), cd.getCalendarNumber(), false, settings);
Mono<String> startTime = EventMessageFormatter
.getHumanReadableTime(event.getStart(), cd.getCalendarNumber(), false, settings);
Mono<String> timezone = CalendarWrapper.INSTANCE.getCalendar(cd)
.map(com.google.api.services.calendar.model.Calendar::getTimeZone)
.defaultIfEmpty("TZ Unknown/Error");
Mono<EventData> eData = DatabaseManager.INSTANCE.getEventData(settings.getGuildID(), event.getId())
.defaultIfEmpty(new EventData())
.cache();
Mono<Boolean> img = eData.filter(EventData::shouldBeSaved)
.flatMap(ed -> ImageUtils.validate(ed.getImageLink(), settings.getPatronGuild()))
.defaultIfEmpty(false);
return Mono.zip(guild, startDate, startTime, timezone, eData, img)
.map(TupleUtils.function((g, sDate, sTime, tz, ed, hasImg) -> {
var embed = EmbedCreateSpec.builder();
if (settings.getBranded())
embed.author(g.getName(), BotSettings.BASE_URL.get(),
g.getIconUrl(Image.Format.PNG).orElse(GlobalVal.getIconUrl()));
else
embed.author("DisCal", BotSettings.BASE_URL.get(), GlobalVal.getIconUrl());
embed.title(Messages.getMessage("Embed.Announcement.Announce.Title", settings));
if (hasImg)
embed.image(ed.getImageLink());
embed.url(event.getHtmlLink());
try {
EventColor ec = EventColor.Companion.fromNameOrHexOrId(event.getColorId());
embed.color(ec.asColor());
} catch (Exception e) {
//I dunno, color probably null.
embed.color(GlobalVal.getDiscalColor());
}
if (settings.getAnnouncementStyle() == AnnouncementStyle.FULL) {
embed.footer(Messages.getMessage("Embed.Announcement.Announce.ID", "%id%",
a.getAnnouncementId().toString(), settings), null);
}
if (a.getInfoOnly() && !"none".equalsIgnoreCase(a.getInfo())) {
//Only send info...
embed.addField(Messages.getMessage("Embed.Announcement.Announce.Info", settings), a.getInfo(), false);
} else {
//Requires all announcement data
if (event.getSummary() != null) {
String summary = event.getSummary();
if (summary.length() > 250) {
summary = summary.substring(0, 250);
summary = summary + " (continues on Google Calendar View)";
}
embed.addField(Messages.getMessage("Embed.Announcement.Announce.Summary", settings), summary, true);
}
if (event.getDescription() != null) {
String description = event.getDescription();
if (description.length() > 250) {
description = description.substring(0, 250);
description = description + " (continues on Google Calendar View)";
}
embed.addField(Messages.getMessage("Embed.Announcement.Announce.Description", settings), description, true);
}
if (settings.getAnnouncementStyle() == AnnouncementStyle.FULL) {
embed.addField(Messages.getMessage("Embed.Announcement.Announce.Date", settings), sDate, true);
embed.addField(Messages.getMessage("Embed.Announcement.Announce.Time", settings), sTime, true);
embed.addField(Messages.getMessage("Embed.Announcement.Announce.TimeZone", settings), tz, true);
} else {
String start = sDate + " at " + sTime + " " + tz;
embed.addField(Messages.getMessage("Embed.Announcement.Announce.Start", settings), start, false);
}
if (event.getLocation() != null && !"".equalsIgnoreCase(event.getLocation())) {
if (event.getLocation().length() > 300) {
String location = event.getLocation().substring(0, 300).trim() + "... (cont. on Google Cal)";
embed.addField(Messages.getMessage("Embed.Event.Confirm.Location", settings), location, true);
} else {
embed.addField(Messages.getMessage("Embed.Event.Confirm.Location", settings), event.getLocation(), true);
}
}
if (settings.getAnnouncementStyle() == AnnouncementStyle.FULL)
embed.addField(Messages.getMessage("Embed.Announcement.Announce.EventID", settings), event.getId(), false);
if (!"None".equalsIgnoreCase(a.getInfo()) && !"".equalsIgnoreCase(a.getInfo()))
embed.addField(Messages.getMessage("Embed.Announcement.Announce.Info", settings), a.getInfo(), false);
}
return embed.build();
}));
}
@Deprecated
public static Mono<Void> sendAnnouncementMessage(Announcement a, Event event, CalendarData data,
GuildSettings settings) {
Mono<Guild> guild = DisCalClient.getClient().getGuildById(settings.getGuildID()).cache();
Mono<EmbedCreateSpec> embed = getRealAnnouncementEmbed(a, event, data, settings);
Mono<String> mentions = guild.flatMap(g -> getSubscriberMentions(a, g));
return Mono.zip(guild, embed, mentions)
.flatMap(TupleUtils.function((g, em, men) ->
g.getChannelById(Snowflake.of(a.getAnnouncementChannelId()))
.ofType(GuildMessageChannel.class)
.onErrorResume(ClientException.class, e ->
Mono.just(e.getStatus())
.filter(HttpResponseStatus.NOT_FOUND::equals)
.flatMap(ignored -> DatabaseManager.INSTANCE.deleteAnnouncement(a.getAnnouncementId().toString()))
.then(Mono.empty()))
.flatMap(chan -> {
if (a.getPublish()) {
return Messages.sendMessage(men, em, chan)
.flatMap(Message::publish)
.onErrorResume(e -> Mono.empty());
} else
return Messages.sendMessage(men, em, chan);
})
)).then();
}
public static Mono<Void> sendAnnouncementMessage(Guild guild, Announcement a, Event event, CalendarData data,
GuildSettings settings) {
Mono<EmbedCreateSpec> embed = getRealAnnouncementEmbed(a, event, data, settings);
Mono<String> mentions = getSubscriberMentions(a, guild);
return Mono.zip(embed, mentions)
.flatMap(TupleUtils.function((em, men) ->
guild.getChannelById(Snowflake.of(a.getAnnouncementChannelId()))
.ofType(GuildMessageChannel.class)
.onErrorResume(ClientException.class, e ->
Mono.just(e.getStatus())
.filter(HttpResponseStatus.NOT_FOUND::equals)
.flatMap(ignored -> DatabaseManager.INSTANCE.deleteAnnouncement(a.getAnnouncementId().toString()))
.then(Mono.empty()))
.flatMap(chan -> {
if (a.getPublish()) {
return Messages.sendMessage(men, em, chan)
.flatMap(Message::publish)
.onErrorResume(e -> Mono.empty());
} else
return Messages.sendMessage(men, em, chan);
})
)).then();
}
public static Mono<Void> sendAnnouncementDM(Announcement a, Event event, User user, CalendarData data,
GuildSettings settings) {
return DisCalClient.getClient().getGuildById(settings.getGuildID())
.map(g -> Messages.getMessage("Embed.Announcement.Announce.Dm.Message", "%guild%", g.getName(), settings))
.flatMap(msg -> getRealAnnouncementEmbed(a, event, data, settings)
.flatMap(em -> Messages.sendDirectMessage(msg, em, user))
).then();
}
private static String condensedTime(Announcement a) {
return a.getHoursBefore() + "H" + a.getMinutesBefore() + "m";
}
@@ -448,45 +211,4 @@ public class AnnouncementMessageFormatter {
}));
});
}
private static Mono<String> getSubscriberMentions(Announcement a, Guild guild) {
return Mono.defer(() -> {
Mono<List<String>> userMentions = Flux.fromIterable(a.getSubscriberUserIds())
.flatMap(s -> UserUtils.getUserFromID(s, guild))
.map(Member::getNicknameMention)
.onErrorReturn("")
.collectList()
.defaultIfEmpty(new ArrayList<>());
Mono<List<String>> roleMentions = Flux.fromIterable(a.getSubscriberRoleIds())
.flatMap(s -> {
if ("everyone".equalsIgnoreCase(s))
return Mono.just("@everyone");
else if ("here".equalsIgnoreCase(s))
return Mono.just("@here");
else {
return RoleUtils.getRoleFromID(s, guild)
.map(Role::getMention)
.onErrorReturn("");
}
}).collectList()
.defaultIfEmpty(new ArrayList<>());
return Mono.zip(userMentions, roleMentions).map(TupleUtils.function((users, roles) -> {
StringBuilder mentions = new StringBuilder();
mentions.append("Subscribers: ");
for (String s : users) {
mentions.append(s).append(" ");
}
for (String s : roles) {
mentions.append(s).append(" ");
}
return mentions.toString();
}));
});
}
}

View File

@@ -1,255 +0,0 @@
package org.dreamexposure.discal.client.module.announcement;
import com.google.api.services.calendar.Calendar;
import com.google.api.services.calendar.model.Event;
import discord4j.common.util.Snowflake;
import discord4j.core.GatewayDiscordClient;
import discord4j.core.object.entity.Guild;
import org.dreamexposure.discal.client.message.AnnouncementMessageFormatter;
import org.dreamexposure.discal.core.database.DatabaseManager;
import org.dreamexposure.discal.core.enums.announcement.AnnouncementType;
import org.dreamexposure.discal.core.enums.event.EventColor;
import org.dreamexposure.discal.core.object.GuildSettings;
import org.dreamexposure.discal.core.object.announcement.Announcement;
import org.dreamexposure.discal.core.object.calendar.CalendarData;
import org.dreamexposure.discal.core.wrapper.google.EventWrapper;
import org.dreamexposure.discal.core.wrapper.google.GoogleAuthWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.function.TupleUtils;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.dreamexposure.discal.core.utils.GlobalVal.getDEFAULT;
public class AnnouncementThread {
private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
private final GatewayDiscordClient client;
private final Map<Snowflake, Mono<GuildSettings>> allSettings = new ConcurrentHashMap<>();
private final Map<Snowflake, Mono<CalendarData>> calendars = new ConcurrentHashMap<>();
private final Map<Snowflake, Mono<Calendar>> customServices = new ConcurrentHashMap<>();
private final Map<Snowflake, Mono<List<Event>>> allEvents = new ConcurrentHashMap<>();
private final Map<Integer, Mono<Calendar>> discalServices = new ConcurrentHashMap<>();
private final long maxDifferenceMs = Duration.ofMinutes(5).toMillis();
public AnnouncementThread(GatewayDiscordClient client) {
this.client = client;
}
public Mono<Void> run() {
//Get the credentials and cache them
Mono<Void> getCredsMono = GoogleAuthWrapper.INSTANCE.credentialsCount()
.flatMapMany(i -> Flux.range(0, i))
.map(index -> {
this.discalServices.put(index, GoogleAuthWrapper.INSTANCE.getCalendarService(index).cache());
return index;
}).then();
//Actually do announcements
Mono<Void> doAnnMono = this.client.getGuilds()
.flatMap(guild -> DatabaseManager.INSTANCE.getEnabledAnnouncements(guild.getId())
.flatMapMany(Flux::fromIterable)
.flatMap(a -> {
final Mono<GuildSettings> s = this.getSettings(a).cache();
final Mono<CalendarData> cd = this.getCalendarData(a).cache();
final Mono<Calendar> se = cd.flatMap(calData -> s.flatMap(gs -> this.getService(calData)));
return Mono.zip(s, cd, se)
.flatMap(TupleUtils.function((settings, calData, service) -> {
switch (a.getModifier()) {
case BEFORE:
return this.handleBeforeModifier(guild, a, settings, calData, service);
case DURING:
return this.handleDuringModifier(guild, a, settings, calData, service);
case END:
return this.handleEndModifier(guild, a, settings, calData, service);
default:
return Mono.empty();
}
}));
})
.doOnError(e -> LOGGER.error(getDEFAULT(), "Announcement error", e))
.onErrorResume(e -> Mono.empty())
)
.doOnError(e -> LOGGER.error(getDEFAULT(), "Announcement error", e))
.onErrorResume(e -> Mono.empty())
.doFinally(ignore -> {
this.allSettings.clear();
this.calendars.clear();
this.customServices.clear();
this.allEvents.clear();
}).then();
//Finally execute those two chains, in order.
return getCredsMono.then(doAnnMono);
}
//Modifier handling
private Mono<Void> handleBeforeModifier(Guild guild, Announcement a, GuildSettings settings, CalendarData calData,
Calendar service) {
switch (a.getType()) {
case SPECIFIC:
return EventWrapper.INSTANCE.getEvent(calData, a.getEventId())
.switchIfEmpty(DatabaseManager.INSTANCE.deleteAnnouncement(a.getAnnouncementId().toString())
.then(Mono.empty())
).flatMap(e -> this.inRangeSpecific(a, e)
.flatMap(inRange -> {
if (inRange) {
return AnnouncementMessageFormatter
.sendAnnouncementMessage(guild, a, e, calData, settings)
.then(DatabaseManager
.INSTANCE.deleteAnnouncement(a.getAnnouncementId().toString())
);
} else {
return Mono.empty(); //Not in range, but still valid.
}
}))
.then();
case UNIVERSAL:
return this.getEvents(calData, service)
.flatMapMany(Flux::fromIterable)
.filter(e -> this.isInRange(a, e))
.flatMap(e -> AnnouncementMessageFormatter
.sendAnnouncementMessage(guild, a, e, calData, settings))
.then();
case COLOR:
return this.getEvents(calData, service)
.flatMapMany(Flux::fromIterable)
.filter(e -> e.getColorId() != null
&& a.getEventColor().equals(EventColor
.Companion.fromNameOrHexOrId(e.getColorId())))
.filter(e -> this.isInRange(a, e))
.flatMap(e -> AnnouncementMessageFormatter
.sendAnnouncementMessage(guild, a, e, calData, settings))
.then();
case RECUR:
return this.getEvents(calData, service)
.flatMapMany(Flux::fromIterable)
.filter(e -> e.getId().contains("_") && e.getId().split("_")[0].equals(a.getEventId()))
.filter(e -> this.isInRange(a, e))
.flatMap(e -> AnnouncementMessageFormatter
.sendAnnouncementMessage(guild, a, e, calData, settings))
.then();
default:
return Mono.empty();
}
}
//TODO: Actually support this.
private Mono<Void> handleDuringModifier(Guild guild, Announcement a, GuildSettings settings, CalendarData calData,
Calendar service) {
switch (a.getType()) {
case SPECIFIC:
case UNIVERSAL:
case COLOR:
case RECUR:
default:
return Mono.empty();
}
}
//TODO: Actually support this too
private Mono<Void> handleEndModifier(Guild guild, Announcement a, GuildSettings settings, CalendarData calData,
Calendar service) {
switch (a.getType()) {
case SPECIFIC:
case UNIVERSAL:
case COLOR:
case RECUR:
default:
return Mono.empty();
}
}
//Utility
private Mono<Boolean> inRangeSpecific(Announcement a, Event e) {
return Mono.defer(() -> {
long announcementTimeMs = Integer.toUnsignedLong(a.getMinutesBefore() + (a.getHoursBefore() * 60)) * 60 * 1000;
long timeUntilEvent = this.getEventStartMs(e) - System.currentTimeMillis();
long difference = timeUntilEvent - announcementTimeMs;
if (difference < 0) {
//Event past, we can delete announcement depending on the type
if (a.getType() == AnnouncementType.SPECIFIC)
return DatabaseManager.INSTANCE.deleteAnnouncement(a.getAnnouncementId().toString())
.thenReturn(false);
return Mono.just(false);
} else {
return Mono.just(difference <= this.maxDifferenceMs);
}
});
}
private boolean isInRange(Announcement a, Event e) {
long announcementTimeMs = Integer.toUnsignedLong(a.getMinutesBefore() + (a.getHoursBefore() * 60)) * 60 * 1000;
long timeUntilEvent = this.getEventStartMs(e) - System.currentTimeMillis();
long difference = timeUntilEvent - announcementTimeMs;
if (difference < 0) {
//Event past, we can delete announcement depending on the type
if (a.getType() == AnnouncementType.SPECIFIC)
return false; //Shouldn't even be used for specific types...
return false;
} else {
return difference <= this.maxDifferenceMs;
}
}
private long getEventStartMs(Event e) {
if (e.getStart().getDateTime() != null)
return e.getStart().getDateTime().getValue();
else
return e.getStart().getDate().getValue();
}
private Mono<GuildSettings> getSettings(Announcement a) {
if (!this.allSettings.containsKey(a.getGuildId()))
this.allSettings.put(a.getGuildId(), DatabaseManager.INSTANCE.getSettings(a.getGuildId()).cache());
return this.allSettings.get(a.getGuildId());
}
//TODO: Allow multiple calendar support
private Mono<CalendarData> getCalendarData(Announcement a) {
if (!this.calendars.containsKey(a.getGuildId()))
this.calendars.put(a.getGuildId(), DatabaseManager.INSTANCE.getMainCalendar(a.getGuildId()).cache());
return this.calendars.get(a.getGuildId());
}
//TODO: Just redo like literally everything
private Mono<Calendar> getService(CalendarData cd) {
if (cd.getExternal()) {
if (!this.customServices.containsKey(cd.getGuildId()))
this.customServices.put(cd.getGuildId(), GoogleAuthWrapper.INSTANCE.getCalendarService(cd).cache());
return this.customServices.get(cd.getGuildId());
}
return GoogleAuthWrapper.INSTANCE.getCalendarService(cd.getCredentialId());
}
private Mono<List<Event>> getEvents(CalendarData cd, Calendar service) {
if (!this.allEvents.containsKey(cd.getGuildId())) {
Mono<List<Event>> events = EventWrapper.INSTANCE.getEvents(cd, service, 15, System.currentTimeMillis()).cache();
this.allEvents.put(cd.getGuildId(), events);
}
return this.allEvents.get(cd.getGuildId());
}
}

View File

@@ -1 +0,0 @@
package org.dreamexposure.discal.client.module.announcement;

View File

@@ -97,7 +97,6 @@ public class AnnouncementCommand implements Command {
info.getSubCommands().put("info", "Sets an additional info.");
info.getSubCommands().put("enable", "Enables or Disables the announcement (alias for `disable`)");
info.getSubCommands().put("disable", "Enables or Disables the announcement (alias for `enable`)");
info.getSubCommands().put("infoOnly", "Allows for setting an announcement to ONLY display the 'extra info'");
info.getSubCommands().put("publish", "Allows for the event to be published if posted in a news channel");
return info;
@@ -174,8 +173,6 @@ public class AnnouncementCommand implements Command {
case "disable":
case "disabled":
return this.moduleEnable(args, event, settings);
case "infoonly":
return this.moduleInfoOnly(args, event, settings);
case "channel":
return this.moduleChannel(args, event, settings);
case "color":
@@ -574,7 +571,7 @@ public class AnnouncementCommand implements Command {
+ Messages.getMessage("Embed.Announcement.Subscribe.Roles", "%roles%",
subRoleString, settings))
.footer(Messages.getMessage("Embed.Announcement.Subscribe.Footer", "%id%",
a.getAnnouncementId().toString(), settings), null)
a.getId().toString(), settings), null)
.build();
return DatabaseManager.INSTANCE.updateAnnouncement(a).thenReturn(embed);
@@ -635,7 +632,7 @@ public class AnnouncementCommand implements Command {
+ Messages.getMessage("Embed.Announcement.Subscribe.Roles", "%roles%",
subRoleString, settings))
.footer(Messages.getMessage("Embed.Announcement.Subscribe.Footer", "%id%",
a.getAnnouncementId().toString(), settings), null)
a.getId().toString(), settings), null)
.build();
return Mono.when(deleteUserMessage, deleteCreatorMessage).thenReturn(embed);
@@ -840,7 +837,7 @@ public class AnnouncementCommand implements Command {
+ Messages.getMessage("Embed.Announcement.Unsubscribe.Roles", "%roles%",
subRoleString, settings))
.footer(Messages.getMessage("Embed.Announcement.Unsubscribe.Footer", "%id%",
a.getAnnouncementId().toString(), settings), null)
a.getId().toString(), settings), null)
.build();
@@ -902,7 +899,7 @@ public class AnnouncementCommand implements Command {
+ Messages.getMessage("Embed.Announcement.Unsubscribe.Roles", "%roles%",
subRoleString, settings))
.footer(Messages.getMessage("Embed.Announcement.Unsubscribe.Footer", "%id%",
a.getAnnouncementId().toString(), settings), null)
a.getId().toString(), settings), null)
.build();
@@ -1245,41 +1242,6 @@ public class AnnouncementCommand implements Command {
}).then();
}
private Mono<Void> moduleInfoOnly(String[] args, MessageCreateEvent event, GuildSettings settings) {
return Mono.defer(() -> {
if (AnnouncementCreator.getCreator().hasAnnouncement(settings.getGuildID())) {
Announcement a = AnnouncementCreator.getCreator().getAnnouncement(settings.getGuildID());
Mono<Void> deleteUserMessage = Messages.deleteMessage(event);
Mono<Void> deleteCreatorMessage = Messages.deleteMessage(a.getCreatorMessage());
return Mono.when(deleteUserMessage, deleteCreatorMessage)
.then(AnnouncementMessageFormatter.getFormatAnnouncementEmbed(a, settings))
.flatMap(em -> Messages.sendMessage(
Messages.getMessage("Announcement.InfoOnly.Creator", settings), em, event))
.doOnNext(a::setCreatorMessage);
} else if (args.length == 2) {
return AnnouncementUtils.announcementExists(args[1], settings.getGuildID()).flatMap(exists -> {
if (exists) {
UUID id = UUID.fromString(args[1]);
AtomicBoolean io = new AtomicBoolean(false); //This has got to be tested...
return DatabaseManager.INSTANCE.getAnnouncement(id, settings.getGuildID())
.doOnNext(a -> a.setInfoOnly(!a.getInfoOnly()))
.doOnNext(a -> io.set(a.getInfoOnly()))
.flatMap(DatabaseManager.INSTANCE::updateAnnouncement)
.map(i -> Messages.getMessage("Announcement.InfoOnly.Success", "%value%", io.get() + "", settings))
.flatMap(msg -> Messages.sendMessage(msg, event));
} else {
return Messages.sendMessage(
Messages.getMessage("Creator.Announcement.CannotFind.Announcement", settings), event);
}
});
} else {
return Messages.sendMessage(Messages.getMessage("Announcement.InfoOnly.Specify", settings), event);
}
}).then();
}
private Mono<Void> modulePublish(MessageCreateEvent event, GuildSettings settings) {
return Mono.defer(() -> {
if (AnnouncementCreator.getCreator().hasAnnouncement(settings.getGuildID())) {

View File

@@ -24,7 +24,6 @@ import io.lettuce.core.RedisURI
import org.dreamexposure.discal.Application
import org.dreamexposure.discal.client.listeners.discord.*
import org.dreamexposure.discal.client.message.Messages
import org.dreamexposure.discal.client.module.announcement.AnnouncementThread
import org.dreamexposure.discal.client.module.command.*
import org.dreamexposure.discal.client.service.TimeManager
import org.dreamexposure.discal.core.`object`.BotSettings
@@ -34,10 +33,8 @@ import org.dreamexposure.discal.core.utils.GlobalVal.DEFAULT
import org.dreamexposure.discal.core.utils.GlobalVal.STATUS
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.stereotype.Component
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import java.io.FileReader
import java.time.Duration
import java.util.*
import javax.annotation.PreDestroy
import kotlin.system.exitProcess
@@ -112,15 +109,7 @@ class DisCalClient {
.on(ChatInputInteractionEvent::class.java, slashCommandListener::handle)
.then()
val startAnnouncement = Flux.interval(Duration.ofMinutes(5))
.onBackpressureBuffer()
.flatMap {
AnnouncementThread(client).run().doOnError {
LOGGER.error("Announcement error", it)
}.onErrorResume { Mono.empty() }
}
Mono.`when`(onReady, onRoleDelete, onCommand, onSlashCommand, startAnnouncement)
Mono.`when`(onReady, onRoleDelete, onCommand, onSlashCommand)
}.block()
}
}

View File

@@ -6,6 +6,7 @@ import discord4j.core.`object`.entity.channel.GuildChannel
import discord4j.core.spec.EmbedCreateSpec
import org.dreamexposure.discal.core.`object`.announcement.Announcement
import org.dreamexposure.discal.core.entities.Event
import org.dreamexposure.discal.core.enums.announcement.AnnouncementStyle
import org.dreamexposure.discal.core.enums.announcement.AnnouncementType
import org.dreamexposure.discal.core.extensions.asDiscordTimestamp
import org.dreamexposure.discal.core.extensions.discord4j.getSettings
@@ -14,6 +15,16 @@ import reactor.core.publisher.Mono
import reactor.function.TupleUtils
object AnnouncementEmbed : EmbedMaker {
fun determine(ann: Announcement, event: Event, guild: Guild): Mono<EmbedCreateSpec> {
return guild.getSettings().flatMap { settings ->
when (settings.announcementStyle) {
AnnouncementStyle.FULL -> full(ann, event, guild)
AnnouncementStyle.SIMPLE -> simple(ann, event, guild)
AnnouncementStyle.EVENT -> event(ann, event, guild)
}
}
}
fun full(ann: Announcement, event: Event, guild: Guild): Mono<EmbedCreateSpec> {
return guild.getSettings().map { settings ->
val builder = defaultBuilder(guild, settings)
@@ -52,7 +63,7 @@ object AnnouncementEmbed : EmbedMaker {
builder.image(event.image)
}
builder.footer(getMessage("announcement", "full.footer", settings, ann.announcementId.toString()), null)
builder.footer(getMessage("announcement", "full.footer", settings, ann.id.toString()), null)
builder.build()
}
@@ -84,7 +95,7 @@ object AnnouncementEmbed : EmbedMaker {
builder.image(event.image)
}
builder.footer(getMessage("announcement", "simple.footer", settings, ann.announcementId.toString()), null)
builder.footer(getMessage("announcement", "simple.footer", settings, ann.id.toString()), null)
builder.build()
}
@@ -127,7 +138,7 @@ object AnnouncementEmbed : EmbedMaker {
builder.image(event.image)
}
builder.footer(getMessage("announcement", "event.footer", settings, ann.announcementId.toString()), null)
builder.footer(getMessage("announcement", "event.footer", settings, ann.id.toString()), null)
builder.build()
}
@@ -137,7 +148,7 @@ object AnnouncementEmbed : EmbedMaker {
return guild.getSettings().map { settings ->
val builder = defaultBuilder(guild, settings)
.title(getMessage("announcement", "con.title", settings))
.addField(getMessage("announcement", "con.field.id", settings), ann.announcementId.toString(), false)
.addField(getMessage("announcement", "con.field.id", settings), ann.id.toString(), false)
.addField(getMessage("announcement", "con.field.time", settings), condensedTime(ann), true)
.addField(getMessage("announcement", "con.field.enabled", settings), "${ann.enabled}", true)
.footer(getMessage("announcement", "con.footer", settings, ann.type.name, ann.modifier.name), null)
@@ -180,7 +191,7 @@ object AnnouncementEmbed : EmbedMaker {
} else
builder.color(GlobalVal.discalColor)
builder.addField(getMessage("announcement", "view.field.id", settings), ann.announcementId.toString(), false)
builder.addField(getMessage("announcement", "view.field.id", settings), ann.id.toString(), false)
.addField(getMessage("announcement", "view.field.enabled", settings), "${ann.enabled}", true)
.addField(getMessage("announcement", "view.field.publish", settings), "${ann.publish}", true)
.build()

View File

@@ -0,0 +1,234 @@
package org.dreamexposure.discal.client.service
import discord4j.common.util.Snowflake
import discord4j.core.`object`.entity.Guild
import discord4j.core.`object`.entity.Message
import discord4j.core.`object`.entity.Role
import discord4j.core.`object`.entity.channel.GuildMessageChannel
import discord4j.core.spec.MessageCreateSpec
import discord4j.rest.http.client.ClientException
import io.netty.handler.codec.http.HttpResponseStatus
import org.dreamexposure.discal.client.DisCalClient
import org.dreamexposure.discal.client.message.embed.AnnouncementEmbed
import org.dreamexposure.discal.core.`object`.announcement.Announcement
import org.dreamexposure.discal.core.`object`.announcement.AnnouncementCache
import org.dreamexposure.discal.core.database.DatabaseManager
import org.dreamexposure.discal.core.entities.Calendar
import org.dreamexposure.discal.core.entities.Event
import org.dreamexposure.discal.core.enums.announcement.AnnouncementModifier
import org.dreamexposure.discal.core.enums.announcement.AnnouncementType.*
import org.dreamexposure.discal.core.extensions.discord4j.getCalendar
import org.dreamexposure.discal.core.logger.LOGGER
import org.dreamexposure.discal.core.utils.GlobalVal
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.ApplicationRunner
import org.springframework.stereotype.Component
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import reactor.function.TupleUtils
import java.time.Duration
import java.util.concurrent.ConcurrentHashMap
@Component
class AnnouncementService : ApplicationRunner {
private val maxDifferenceMs = Duration.ofMinutes(5).toMillis()
private val cached = ConcurrentHashMap<Snowflake, AnnouncementCache>()
// Start
override fun run(args: ApplicationArguments?) {
Flux.interval(Duration.ofMinutes(5))
.onBackpressureBuffer()
.flatMap { doAnnouncementCycle() }
.doOnError { LOGGER.error(GlobalVal.DEFAULT, "!-Announcement run error-!", it) }
.subscribe()
}
// Runner
private fun doAnnouncementCycle(): Mono<Void> {
//TODO: This should come in through DI once other legacy is removed/rewritten
if (DisCalClient.client == null) return Mono.empty()
return DisCalClient.client!!.guilds.flatMap { guild ->
DatabaseManager.getEnabledAnnouncements(guild.id)
.flatMapMany { Flux.fromIterable(it) }
.flatMap { announcement ->
when (announcement.modifier) {
AnnouncementModifier.BEFORE -> handleBeforeModifier(guild, announcement)
AnnouncementModifier.DURING -> handleDuringModifier(guild, announcement)
AnnouncementModifier.END -> handleEndModifier(guild, announcement)
}
}.doOnError {
LOGGER.error(GlobalVal.DEFAULT, "Announcement error", it)
}.onErrorResume { Mono.empty() }
}.doOnError {
LOGGER.error(GlobalVal.DEFAULT, "Announcement error", it)
}.onErrorResume {
Mono.empty()
}.doFinally {
cached.clear()
}.then()
}
// Modifier handling
private fun handleBeforeModifier(guild: Guild, announcement: Announcement): Mono<Void> {
when (announcement.type) {
SPECIFIC -> {
return getCalendar(guild, announcement)
.flatMap { it.getEvent(announcement.eventId) }
//Event announcement is tied to was deleted
.switchIfEmpty(DatabaseManager.deleteAnnouncement(announcement.id.toString()).then(Mono.empty()))
.filterWhen { isInRange(announcement, it) }
.flatMap { sendAnnouncement(guild, announcement, it) }
// Delete specific announcement after posted
.flatMap { DatabaseManager.deleteAnnouncement(announcement.id.toString()) }
.then()
}
UNIVERSAL -> {
return getEvents(guild, announcement)
.filterWhen { isInRange(announcement, it) }
.flatMap { sendAnnouncement(guild, announcement, it) }
.then()
}
COLOR -> {
return getEvents(guild, announcement)
.filter { it.color == announcement.eventColor }
.filterWhen { isInRange(announcement, it) }
.flatMap { sendAnnouncement(guild, announcement, it) }
.then()
}
RECUR -> {
return getEvents(guild, announcement)
.filter { it.eventId.contains("_") && it.eventId.split("_")[0] == announcement.eventId }
.filterWhen { isInRange(announcement, it) }
.flatMap { sendAnnouncement(guild, announcement, it) }
.then()
}
}
}
@Suppress("UNUSED_PARAMETER")
private fun handleDuringModifier(guild: Guild, announcement: Announcement): Mono<Void> {
//TODO: Not yet implemented
return Mono.empty()
}
@Suppress("UNUSED_PARAMETER")
private fun handleEndModifier(guild: Guild, announcement: Announcement): Mono<Void> {
//TODO: Not yet implemented
return Mono.empty()
}
// Utility
private fun isInRange(announcement: Announcement, event: Event): Mono<Boolean> {
val announcementTime = Duration
.ofHours(announcement.hoursBefore.toLong())
.plusMinutes(announcement.minutesBefore.toLong())
.toMillis()
val timeUntilEvent = event.start.minusMillis(System.currentTimeMillis()).toEpochMilli()
val difference = timeUntilEvent - announcementTime
if (difference < 0) {
//event past, delete if specific type
if (announcement.type == SPECIFIC) {
return DatabaseManager.deleteAnnouncement(announcement.id.toString())
.thenReturn(false)
}
return Mono.just(false)
} else return Mono.just(difference <= maxDifferenceMs)
}
private fun sendAnnouncement(guild: Guild, announcement: Announcement, event: Event): Mono<Message> {
val embedMono = AnnouncementEmbed.determine(announcement, event, guild)
val mentionsMono = buildMentions(guild, announcement).onErrorReturn("")
return guild.getChannelById(Snowflake.of(announcement.announcementChannelId))
.ofType(GuildMessageChannel::class.java)
.flatMap { channel ->
Mono.zip(embedMono, mentionsMono).flatMap(TupleUtils.function { embed, mentions ->
if (mentions.isEmpty())
return@function channel.createMessage(embed)
else
return@function channel.createMessage(
MessageCreateSpec.builder()
.content(mentions)
.addEmbed(embed)
.build()
)
}).flatMap { message ->
if (announcement.publish) {
message.publish()
} else Mono.just(message)
}
}.onErrorResume(ClientException::class.java) {
Mono.just(it)
.filter(HttpResponseStatus.NOT_FOUND::equals)
// Channel announcement should post to was deleted
.flatMap { DatabaseManager.deleteAnnouncement(announcement.id.toString()) }
.then(Mono.empty())
}
}
private fun buildMentions(guild: Guild, announcement: Announcement): Mono<String> {
val userMentions = Flux.fromIterable(announcement.subscriberUserIds)
.flatMap { guild.getMemberById(Snowflake.of(it)) }
.map { it.nicknameMention }
.onErrorReturn("")
.collectList()
.defaultIfEmpty(listOf())
val roleMentions = Flux.fromIterable(announcement.subscriberRoleIds)
.flatMap {
if (it.equals("everyone", true)) guild.everyoneRole.map(Role::getMention)
else if (it.equals("here", true)) Mono.just("here")
else guild.getRoleById(Snowflake.of(it)).map(Role::getMention)
}
.onErrorReturn("")
.collectList()
.defaultIfEmpty(listOf())
return Mono.zip(userMentions, roleMentions).map(TupleUtils.function { users, roles ->
if (users.isEmpty() && roles.isEmpty()) ""
else {
val mentions = StringBuilder()
mentions.append("Subscribers: ")
for (u in users) mentions.append("$u ")
for (r in roles) mentions.append("$r ")
mentions.toString()
}
})
}
// Cache things
private fun getCalendar(guild: Guild, announcement: Announcement): Mono<Calendar> {
val cached = getCached(announcement.guildId)
return if (!cached.calendars.contains(announcement.calendarNumber)) {
guild.getCalendar(announcement.calendarNumber)
.doOnNext { cached.calendars[it.calendarNumber] = it }
} else Mono.justOrEmpty(cached.calendars[announcement.calendarNumber])
}
private fun getEvents(guild: Guild, announcement: Announcement): Flux<Event> {
val cached = getCached(announcement.guildId)
return if (!cached.events.contains(announcement.calendarNumber)) {
getCalendar(guild, announcement).flatMapMany {
it.getUpcomingEvents(20).cache()
}
} else cached.events[announcement.calendarNumber]!!
}
private fun getCached(guildId: Snowflake): AnnouncementCache {
if (!cached.contains(guildId))
cached[guildId] = AnnouncementCache(guildId)
return cached[guildId]!!
}
}

View File

@@ -4,20 +4,10 @@ import discord4j.common.util.Snowflake;
import discord4j.core.event.domain.message.MessageCreateEvent;
import discord4j.core.object.entity.Member;
import discord4j.core.object.entity.Role;
import discord4j.discordjson.json.GuildUpdateData;
import discord4j.discordjson.json.MemberData;
import discord4j.discordjson.json.RoleData;
import discord4j.rest.entity.RestGuild;
import discord4j.rest.entity.RestMember;
import discord4j.rest.util.Permission;
import discord4j.rest.util.PermissionSet;
import org.dreamexposure.discal.core.object.BotSettings;
import org.dreamexposure.discal.core.object.GuildSettings;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.function.Predicate;
/**
* Created by Nova Fox on 1/19/17.
* Website: www.cloudcraftgaming.com
@@ -49,64 +39,6 @@ public class PermissionChecker {
});
}
public static Mono<Boolean> hasDisCalRole(final Member member, final GuildSettings settings) {
if ("everyone".equalsIgnoreCase(settings.getControlRole()))
return Mono.just(true);
if (Snowflake.of(settings.getControlRole()).equals(settings.getGuildID())) //also everyone
return Mono.just(true);
//User doesn't need bot control role if they have admin permissions.
final Mono<Boolean> hasAdmin = Mono.just(member).flatMap(Member::getBasePermissions).map(perms ->
perms.contains(Permission.ADMINISTRATOR) || perms.contains(Permission.MANAGE_GUILD));
return hasAdmin.flatMap(has -> {
if (has) {
return Mono.just(true);
} else {
return Mono.just(member).flatMapMany(Member::getRoles)
.map(Role::getId)
.any(id -> id.equals(Snowflake.of(settings.getControlRole())));
}
});
}
@Deprecated
public static Mono<Boolean> hasSufficientRole(final MessageCreateEvent event, final GuildSettings settings) {
if ("everyone".equalsIgnoreCase(settings.getControlRole()))
return Mono.just(true);
if (Snowflake.of(settings.getControlRole()).equals(settings.getGuildID())) //also everyone
return Mono.just(true);
return Mono.justOrEmpty(event.getMember())
.flatMapMany(Member::getRoles)
.map(Role::getId)
.any(snowflake -> snowflake.equals(Snowflake.of(settings.getControlRole())));
}
@Deprecated
public static Mono<Boolean> hasSufficientRole(final Member member, final GuildSettings settings) {
if ("everyone".equalsIgnoreCase(settings.getControlRole()))
return Mono.just(true);
if (Snowflake.of(settings.getControlRole()).equals(settings.getGuildID())) //also everyone
return Mono.just(true);
return Mono.from(member.getRoles()
.map(Role::getId)
.any(snowflake -> snowflake.equals(Snowflake.of(settings.getControlRole())))
);
}
public static Mono<Boolean> hasSufficientRole(final RestMember member, final GuildSettings settings) {
if ("everyone".equalsIgnoreCase(settings.getControlRole()))
return Mono.just(true);
if (Snowflake.of(settings.getControlRole()).equals(settings.getGuildID())) //also everyone
return Mono.just(true);
return member.getData()
.map(MemberData::roles)
.map(roles -> roles.contains(settings.getControlRole()));
}
public static Mono<Boolean> hasManageServerRole(final MessageCreateEvent event) {
return Mono.justOrEmpty(event.getMember())
.flatMap(Member::getBasePermissions)
@@ -114,38 +46,4 @@ public class PermissionChecker {
|| perms.contains(Permission.ADMINISTRATOR))
.defaultIfEmpty(false);
}
public static Mono<Boolean> hasManageServerRole(final Member m) {
return m.getBasePermissions()
.map(perms -> perms.contains(Permission.MANAGE_GUILD)
|| perms.contains(Permission.ADMINISTRATOR)
);
}
public static Mono<Boolean> hasManageServerRole(final RestMember m, final RestGuild g) {
return hasPermissions(m, g, permissions ->
permissions.contains(Permission.MANAGE_GUILD)
|| permissions.contains(Permission.ADMINISTRATOR));
}
public static Mono<Boolean> hasPermissions(final RestMember m, final RestGuild g,
final Predicate<PermissionSet> pred) {
return m.getData().flatMap(memberData ->
g.getData().map(GuildUpdateData::roles)
.flatMapMany(Flux::fromIterable)
.filter(roleData -> memberData.roles().contains(roleData.id()))
.map(RoleData::permissions)
.reduce(0L, (perm, accumulator) -> accumulator | perm)
.map(PermissionSet::of)
.map(pred::test)
);
}
public static Mono<Boolean> botHasMessageManagePerms(final MessageCreateEvent event) {
return event.getGuild()
.flatMap(guild -> guild.getMemberById(Snowflake.of(BotSettings.ID.get()))
.flatMap(Member::getBasePermissions)
.map(perms -> perms.contains(Permission.MANAGE_MESSAGES))
);
}
}

View File

@@ -249,7 +249,7 @@ object DatabaseManager {
val query = "SELECT * FROM ${Tables.ANNOUNCEMENTS.table} WHERE ANNOUNCEMENT_ID = ?"
Mono.from(c.createStatement(query)
.bind(0, announcement.announcementId.toString())
.bind(0, announcement.id.toString())
.execute()
).flatMapMany { res ->
res.map { row, _ -> row }
@@ -259,7 +259,7 @@ object DatabaseManager {
CALENDAR_NUMBER = ?, SUBSCRIBERS_ROLE = ?, SUBSCRIBERS_USER = ?, CHANNEL_ID = ?,
ANNOUNCEMENT_TYPE = ?, MODIFIER = ?, EVENT_ID = ?, EVENT_COLOR = ?,
HOURS_BEFORE = ?, MINUTES_BEFORE = ?,
INFO = ?, ENABLED = ?, INFO_ONLY = ?, PUBLISH = ?
INFO = ?, ENABLED = ?, PUBLISH = ?
WHERE ANNOUNCEMENT_ID = ?
""".trimMargin()
@@ -276,9 +276,8 @@ object DatabaseManager {
.bind(9, announcement.minutesBefore)
.bind(10, announcement.info)
.bind(11, announcement.enabled)
.bind(12, announcement.infoOnly)
.bind(13, announcement.publish)
.bind(14, announcement.announcementId.toString())
.bind(12, announcement.publish)
.bind(13, announcement.id.toString())
.execute()
).flatMapMany(Result::getRowsUpdated)
.hasElements()
@@ -287,12 +286,12 @@ object DatabaseManager {
val insertCommand = """INSERT INTO ${Tables.ANNOUNCEMENTS.table}
(ANNOUNCEMENT_ID, CALENDAR_NUMBER, GUILD_ID, SUBSCRIBERS_ROLE, SUBSCRIBERS_USER,
CHANNEL_ID, ANNOUNCEMENT_TYPE, MODIFIER, EVENT_ID, EVENT_COLOR,
HOURS_BEFORE, MINUTES_BEFORE, INFO, ENABLED, INFO_ONLY, PUBLISH)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
HOURS_BEFORE, MINUTES_BEFORE, INFO, ENABLED, PUBLISH)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""".trimMargin()
Mono.from(c.createStatement(insertCommand)
.bind(0, announcement.announcementId.toString())
.bind(0, announcement.id.toString())
.bind(1, announcement.calendarNumber)
.bind(2, announcement.guildId.asString())
.bind(3, announcement.subscriberRoleIds.asStringList())
@@ -306,8 +305,7 @@ object DatabaseManager {
.bind(11, announcement.minutesBefore)
.bind(12, announcement.info)
.bind(13, announcement.enabled)
.bind(14, announcement.infoOnly)
.bind(15, announcement.publish)
.bind(14, announcement.publish)
.execute()
).flatMapMany(Result::getRowsUpdated)
.hasElements()
@@ -761,7 +759,6 @@ object DatabaseManager {
a.minutesBefore = row["MINUTES_BEFORE", Int::class.java]!!
a.info = row["INFO", String::class.java]!!
a.enabled = row["ENABLED", Boolean::class.java]!!
a.infoOnly = row["INFO_ONLY", Boolean::class.java]!!
a.publish = row["PUBLISH", Boolean::class.java]!!
a
@@ -799,7 +796,6 @@ object DatabaseManager {
a.minutesBefore = row["MINUTES_BEFORE", Int::class.java]!!
a.info = row["INFO", String::class.java]!!
a.enabled = row["ENABLED", Boolean::class.java]!!
a.infoOnly = row["INFO_ONLY", Boolean::class.java]!!
a.publish = row["PUBLISH", Boolean::class.java]!!
a
@@ -838,7 +834,6 @@ object DatabaseManager {
a.minutesBefore = row["MINUTES_BEFORE", Int::class.java]!!
a.info = row["INFO", String::class.java]!!
a.enabled = row["ENABLED", Boolean::class.java]!!
a.infoOnly = row["INFO_ONLY", Boolean::class.java]!!
a.publish = row["PUBLISH", Boolean::class.java]!!
a
@@ -876,7 +871,6 @@ object DatabaseManager {
a.minutesBefore = row["MINUTES_BEFORE", Int::class.java]!!
a.info = row["INFO", String::class.java]!!
a.enabled = row["ENABLED", Boolean::class.java]!!
a.infoOnly = row["INFO_ONLY", Boolean::class.java]!!
a.publish = row["PUBLISH", Boolean::class.java]!!
a
@@ -915,7 +909,6 @@ object DatabaseManager {
a.minutesBefore = row["MINUTES_BEFORE", Int::class.java]!!
a.info = row["INFO", String::class.java]!!
a.enabled = row["ENABLED", Boolean::class.java]!!
a.infoOnly = row["INFO_ONLY", Boolean::class.java]!!
a.publish = row["PUBLISH", Boolean::class.java]!!
a
@@ -953,7 +946,6 @@ object DatabaseManager {
a.minutesBefore = row["MINUTES_BEFORE", Int::class.java]!!
a.info = row["INFO", String::class.java]!!
a.enabled = row["ENABLED", Boolean::class.java]!!
a.infoOnly = row["INFO_ONLY", Boolean::class.java]!!
a.publish = row["PUBLISH", Boolean::class.java]!!
a
@@ -991,7 +983,6 @@ object DatabaseManager {
a.minutesBefore = row["MINUTES_BEFORE", Int::class.java]!!
a.info = row["INFO", String::class.java]!!
a.enabled = row["ENABLED", Boolean::class.java]!!
a.infoOnly = row["INFO_ONLY", Boolean::class.java]!!
a.publish = row["PUBLISH", Boolean::class.java]!!
a
@@ -1030,7 +1021,6 @@ object DatabaseManager {
a.minutesBefore = row["MINUTES_BEFORE", Int::class.java]!!
a.info = row["INFO", String::class.java]!!
a.enabled = row["ENABLED", Boolean::class.java]!!
a.infoOnly = row["INFO_ONLY", Boolean::class.java]!!
a.publish = row["PUBLISH", Boolean::class.java]!!
a

View File

@@ -19,8 +19,7 @@ data class Announcement(
val guildId: Snowflake,
) {
@Serializable(with = UUIDasStringSerializer::class)
@SerialName("id")
var announcementId: UUID = UUID.randomUUID()
var id: UUID = UUID.randomUUID()
private set
@SerialName("subscriber_roles")
@@ -52,9 +51,6 @@ data class Announcement(
var enabled = true
@Deprecated(message = "Info only support is dropping in favor of a guild-wide announcement-style")
@SerialName("info_only")
var infoOnly = false
var publish = false
//Stuff for wizards
@@ -68,14 +64,14 @@ data class Announcement(
var lastEdit = System.currentTimeMillis()
constructor(guildId: Snowflake, announcementId: UUID) : this(guildId) {
this.announcementId = announcementId
this.id = announcementId
}
companion object {
fun copy(from: Announcement, copyId: Boolean = false): Announcement {
val to = from.copy()
if (copyId)
to.announcementId = UUID.randomUUID()
to.id = UUID.randomUUID()
//Copy all the other params...
to.subscriberRoleIds.addAll(from.subscriberRoleIds)
@@ -89,7 +85,6 @@ data class Announcement(
to.minutesBefore = from.minutesBefore
to.info = from.info
to.enabled = from.enabled
to.infoOnly = from.infoOnly
to.publish = to.publish
return to

View File

@@ -0,0 +1,13 @@
package org.dreamexposure.discal.core.`object`.announcement
import discord4j.common.util.Snowflake
import org.dreamexposure.discal.core.entities.Calendar
import org.dreamexposure.discal.core.entities.Event
import reactor.core.publisher.Flux
import java.util.concurrent.ConcurrentHashMap
data class AnnouncementCache(
val id: Snowflake,
val calendars: ConcurrentHashMap<Int, Calendar> = ConcurrentHashMap(),
val events: ConcurrentHashMap<Int, Flux<Event>> = ConcurrentHashMap(),
)

View File

@@ -69,7 +69,7 @@ object EventWrapper {
}
fun getEvents(calData: CalendarData, amount: Int, start: Long): Mono<List<Event>> {
return GoogleAuthWrapper.getCalendarService(calData).flatMap { service: Calendar ->
return GoogleAuthWrapper.getCalendarService(calData).flatMap { service ->
Mono.fromCallable {
service.events()
.list(calData.calendarId)
@@ -87,24 +87,6 @@ object EventWrapper {
}.onErrorResume { Mono.empty() }
}
@Deprecated(message = "Deprecated, do not use service directly")
fun getEvents(calData: CalendarData, service: Calendar, amount: Int, start: Long): Mono<List<Event>> {
return Mono.fromCallable {
service.events()
.list(calData.calendarId)
.setMaxResults(amount)
.setTimeMin(DateTime(start))
.setOrderBy("startTime")
.setSingleEvents(true)
.setShowDeleted(false)
.setQuotaUser(calData.guildId.asString())
.execute().items
}.subscribeOn(Schedulers.boundedElastic())
.doOnError {
LOGGER.error(GlobalVal.DEFAULT, "[G.Cal] Event list(2) failure", it)
}.onErrorResume { Mono.empty() }
}
fun getEvents(calData: CalendarData, amount: Int, start: Long, end: Long): Mono<List<Event>> {
return GoogleAuthWrapper.getCalendarService(calData).flatMap { service: Calendar ->
Mono.fromCallable {

View File

@@ -0,0 +1,2 @@
ALTER TABLE ${prefix}announcements
DROP COLUMN INFO_ONLY;

View File

@@ -59,7 +59,6 @@ class CreateAnnouncementEndpoint(val client: DiscordClient) {
announcement.minutesBefore = body.optInt("minutes", 0)
announcement.info = body.optString("info", "N/a")
announcement.infoOnly = body.optBoolean("info_only", false)
announcement.publish = body.optBoolean("publish", false)

View File

@@ -50,7 +50,6 @@ class UpdateAnnouncementEndpoint(val client: DiscordClient) {
ann.hoursBefore = body.optInt("hours", ann.hoursBefore)
ann.minutesBefore = body.optInt("hours", ann.minutesBefore)
ann.info = body.optString("info", ann.info)
ann.infoOnly = body.optBoolean("info_only", ann.infoOnly)
ann.enabled = body.optBoolean("enabled", ann.enabled)
ann.publish = body.optBoolean("publish", ann.publish)