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
5 changes: 4 additions & 1 deletion PP.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ In certain circumstances, you have the following data protection rights:

## Usage of Data

**TJ-Bot** may use stored data, as defined below, to offer different features and services. No usage of data outside of the aformentioned cases will happen and the data is not shared with any third-party site or service.
**TJ-Bot** may use stored data, as defined below, to offer different features and services. No usage of data outside of the aforementioned cases will happen and the data is not shared with any third-party site or service.

### Databases

Expand All @@ -55,8 +55,11 @@ For example, **TJ-Bot** may associate your `user_id` with a `message_id` and a `

**TJ-Bot** may further store data that you explicitly provided for **TJ-Bot** to offer its services. For example the reason of a moderative action when using its moderation commands.

Furthermore, upon utilization of our help service, `user_id`s and `channel_id`s are stored to track when/how many questions a user asks. The data may be stored for up to **30** days.

The stored data is not linked to any information that is personally identifiable.


No other personal information outside of the above mentioned one will be stored. In particular, **TJ-Bot** does not store the content of sent messages.

### Log Files
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ private Features() {
ModerationActionsStore actionsStore = new ModerationActionsStore(database);
ModAuditLogWriter modAuditLogWriter = new ModAuditLogWriter(config);
ScamHistoryStore scamHistoryStore = new ScamHistoryStore(database);
HelpSystemHelper helpSystemHelper = new HelpSystemHelper(config);
HelpSystemHelper helpSystemHelper = new HelpSystemHelper(config, database);

// NOTE The system can add special system relevant commands also by itself,
// hence this list may not necessarily represent the full list of all commands actually
Expand All @@ -75,6 +75,7 @@ private Features() {
features.add(new RemindRoutine(database));
features.add(new ScamHistoryPurgeRoutine(scamHistoryStore));
features.add(new BotMessageCleanup(config));
features.add(new HelpThreadMetadataPurger(database));
features.add(new HelpThreadActivityUpdater(helpSystemHelper));
features
.add(new AutoPruneHelperRoutine(config, helpSystemHelper, modAuditLogWriter, database));
Expand All @@ -88,6 +89,7 @@ private Features() {

// Event receivers
features.add(new RejoinModerationRoleListener(actionsStore, config));
features.add(new OnGuildLeaveCloseThreadListener(database));

// Slash commands
features.add(new LogLevelCommand());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ public final class AskCommand extends SlashCommandAdapter {

private static final String TITLE_OPTION = "title";
private static final String CATEGORY_OPTION = "category";

private final HelpSystemHelper helper;

/**
Expand Down Expand Up @@ -120,6 +119,7 @@ private boolean handleIsValidTitle(@NotNull CharSequence title, @NotNull IReplyC
private @NotNull RestAction<Message> handleEvent(@NotNull InteractionHook eventHook,
@NotNull ThreadChannel threadChannel, @NotNull Member author, @NotNull String title,
@NotNull String category, @NotNull Guild guild) {
helper.writeHelpThreadToDatabase(author, threadChannel);
return sendInitialMessage(guild, threadChannel, author, title, category)
.flatMap(any -> notifyUser(eventHook, threadChannel))
.flatMap(any -> helper.sendExplanationMessage(threadChannel));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
import org.slf4j.LoggerFactory;
import org.togetherjava.tjbot.config.Config;
import org.togetherjava.tjbot.config.HelpSystemConfig;
import org.togetherjava.tjbot.db.Database;
import org.togetherjava.tjbot.db.generated.tables.HelpThreads;
import org.togetherjava.tjbot.db.generated.tables.records.HelpThreadsRecord;

import java.awt.Color;
import java.io.InputStream;
Expand Down Expand Up @@ -47,14 +50,16 @@ public final class HelpSystemHelper {
private final Predicate<String> isStagingChannelName;
private final String stagingChannelPattern;
private final String categoryRoleSuffix;
private final Database database;

/**
* Creates a new instance.
*
* @param config the config to use
*/
public HelpSystemHelper(@NotNull Config config) {
public HelpSystemHelper(@NotNull Config config, @NotNull Database database) {
HelpSystemConfig helpConfig = config.getHelpSystem();
this.database = database;

overviewChannelPattern = helpConfig.getOverviewChannelPattern();
isOverviewChannelName = Pattern.compile(overviewChannelPattern).asMatchPredicate();
Expand Down Expand Up @@ -99,6 +104,18 @@ RestAction<Message> sendExplanationMessage(@NotNull MessageChannel threadChannel
return action.setEmbeds(embeds);
}

public void writeHelpThreadToDatabase(Member author, ThreadChannel threadChannel) {
database.write(content -> {
HelpThreadsRecord helpThreadsRecord = content.newRecord(HelpThreads.HELP_THREADS)
.setAuthorId(author.getIdLong())
.setChannelId(threadChannel.getIdLong())
.setCreatedAt(threadChannel.getTimeCreated().toInstant());
if (helpThreadsRecord.update() == 0) {
helpThreadsRecord.insert();
}
});
}

private static @NotNull MessageEmbed embedWith(@NotNull CharSequence message) {
return embedWith(message, null);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.togetherjava.tjbot.commands.help;

import net.dv8tion.jda.api.JDA;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.togetherjava.tjbot.commands.Routine;
import org.togetherjava.tjbot.db.Database;
import org.togetherjava.tjbot.db.generated.tables.HelpThreads;
import java.time.Instant;
import java.time.Period;
import java.util.concurrent.TimeUnit;

/**
* Purge Routine to get rid of old thread creations in the database.
*/
public class HelpThreadMetadataPurger implements Routine {
private final Database database;
private static final Logger logger = LoggerFactory.getLogger(HelpThreadMetadataPurger.class);
private static final Period DELETE_MESSAGE_RECORDS_AFTER = Period.ofDays(30);

public HelpThreadMetadataPurger(@NotNull Database database) {
this.database = database;
}

@Override
public @NotNull Schedule createSchedule() {
return new Schedule(ScheduleMode.FIXED_RATE, 0, 4, TimeUnit.HOURS);
}

@Override
public void runRoutine(@NotNull JDA jda) {
int recordsDeleted =
database.writeAndProvide(content -> content.deleteFrom(HelpThreads.HELP_THREADS))
.where(HelpThreads.HELP_THREADS.CREATED_AT
.lessOrEqual(Instant.now().minus(DELETE_MESSAGE_RECORDS_AFTER)))
.execute();
if (recordsDeleted > 0) {
logger.debug("{} old thread channels deleted because they are older than {}.",
recordsDeleted, DELETE_MESSAGE_RECORDS_AFTER);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ private Optional<HelpThread> getLastHelpThreadIfOnCooldown(long userId) {
private @NotNull RestAction<?> handleEvent(@NotNull ThreadChannel threadChannel,
@NotNull Message message, @NotNull String title) {
Member author = message.getMember();
helper.writeHelpThreadToDatabase(author, threadChannel);
userIdToLastHelpThread.put(author.getIdLong(),
new HelpThread(threadChannel.getIdLong(), author.getIdLong(), Instant.now()));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.togetherjava.tjbot.commands.help;

import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.ThreadChannel;
import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.togetherjava.tjbot.commands.EventReceiver;
import org.togetherjava.tjbot.db.Database;
import org.togetherjava.tjbot.db.generated.tables.HelpThreads;

import javax.annotation.Nonnull;
import java.util.*;

/**
* Remove all thread channels associated to a user when they leave the guild.
*/
public class OnGuildLeaveCloseThreadListener extends ListenerAdapter implements EventReceiver {
private static final Logger logger =
LoggerFactory.getLogger(OnGuildLeaveCloseThreadListener.class);
private final Database database;

public OnGuildLeaveCloseThreadListener(@NotNull Database database) {
this.database = database;
}

@Override
public void onGuildMemberRemove(@Nonnull GuildMemberRemoveEvent leaveEvent) {
Set<Long> channelIds = getThreadsCreatedByLeaver(leaveEvent.getUser().getIdLong());
for (long channelId : channelIds) {
closeThread(channelId, leaveEvent);
}
}

public Set<Long> getThreadsCreatedByLeaver(long leaverId) {
return new HashSet<>(database
.readTransaction(context -> context.select(HelpThreads.HELP_THREADS.CHANNEL_ID))
.from(HelpThreads.HELP_THREADS)
.where(HelpThreads.HELP_THREADS.AUTHOR_ID.eq(leaverId))
.fetch(databaseMapper -> databaseMapper.getValue(HelpThreads.HELP_THREADS.CHANNEL_ID)));
}

public void closeThread(long channelId, @NotNull GuildMemberRemoveEvent leaveEvent) {
ThreadChannel threadChannel = leaveEvent.getGuild().getThreadChannelById(channelId);
if (threadChannel == null) {
logger.warn(
"Attempted to archive thread id: '{}' but could not find thread in guild: '{}'.",
channelId, leaveEvent.getGuild().getName());
return;
}
MessageEmbed embed = new EmbedBuilder().setTitle("OP left")
.setDescription("Closing thread...")
.setColor(HelpSystemHelper.AMBIENT_COLOR)
.build();
threadChannel.sendMessageEmbeds(embed)
.flatMap(any -> threadChannel.getManager().setArchived(true))
.queue();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CREATE TABLE help_threads
(
channel_id BIGINT NOT NULL PRIMARY KEY,
author_id BIGINT NOT NULL,
created_at TIMESTAMP NOT NULL
)