Warning: session_start(): open(/var/lib/php/sessions/sess_5ir15qnq8gghh0d1n4opol2o8d, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
import net.dv8tion.jda.core.events.guild.member.*;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.requests.restaction.MessageAction;
import net.dv8tion.jda.core.requests.RestAction;
import java.util.function.Consumer;
switchable S discordToken;
S discordBotName;
long discordBotID;
bool reactToBots = true;
transient bool sendToPostedLinesCRUD = true;
transient JDA discord;
transient Color discordImageEmbedMysteriousLineColor = colorFromHex("36393f");
transient L> onPostedInChannel = syncList();
//transient bool debugPosting;
// when a discord action failed
transient Consumer onQueueError = error -> {
temp enter();
printStackTrace(error);
};
void startDiscord {
if (!discordEnabled()) ret with print("Not enabled");
vm_cleanPrints(); // avoid Swing Unicode problem
logModuleOutput(); // good idea for chat bots
// TODO: log JDA output going to System.out/err
discord = discordBot(new ListenerAdapter {
@Override
public void onMessageUpdate(MessageUpdateEvent e) pcall {
temp enter();
ret if !discordEnabled() || !licensed();
Message msg = e.getMessage();
long msgID = msg.getIdLong();
User user = e.getAuthor();
long userID = user == null ? 0 : user.getIdLong();
S content = e.getMessage().getContentRaw();
MessageChannel channel = e.getChannel();
long channelID = channel.getIdLong();
S rendered = msgID + ": " + content;
//print("Message edited: " + rendered);
vmBus_send editedDiscordMessage(
litmapparams(event := e, module := dm_me(), +msgID, +msg, +content,
+channelID, +channel, +user, +userID));
O lineConcept = dm_discord_lineForMsgID_unimported(msgID);
if (lineConcept == null) ret; // logging module not enabled
call(lineConcept, '_setField, editedText := content);
}
@Override
public void onMessageReceived(MessageReceivedEvent e) pcall {
temp enter();
ret if !discordEnabled() || !licensed();
// send out raw event
vmBus_send discord_onMessageReceived(module(), e);
User user = e.getAuthor();
if (!reactToUser(user)) ret with print("Not reacting to user " + user);
bool bot = user.isBot();
long msgID = e.getMessage().getIdLong();
long userID = user.getIdLong();
Guild guild = e.getGuild();
long guildID = toLong(call(guild, 'getIdLong);
Member member = e.getMember();
S userName = member == null ? null : member.getNickname(); // the changeable nick name - null sometimes?
if (userName == null && member != null) userName = member.getEffectiveName();
if (userName == null) userName = e.getAuthor().getName();
final Message msg = e.getMessage();
MessageChannel channel = e.getChannel();
long channelID = channel.getIdLong();
//print("Channel type: " + e.getChannelType());
bool isPrivate = e.getChannelType() == ChannelType.PRIVATE;
S content = trim(msg.getContentRaw());
print("Msg from " + userName + ": " + content);
vmBus_send incomingDiscordMessage(
litmapparams(fromBot := bot, module := dm_me(), +msgID, +userID, +userName, event := e, +guildID, +guild, +member,
+msg,
+content, +isPrivate, +channelID, +channel));
}
@Override
public void onMessageReactionAdd(MessageReactionAddEvent e) pcall {
temp enter();
ret if !discordEnabled() || !licensed();
MessageReaction r = e.getReaction();
User user = e.getUser();
if (!reactToUser(user)) ret;
bool bot = user.isBot();
long msgID = r.getMessageIdLong();
S emoji = r.getReactionEmote().getName();
vmBus_send('incomingDiscordReaction, litmapparams(
reaction := r,
fromBot := bot, +user, userID := user.getIdLong(),
module := dm_me(), +msgID, +emoji
));
}
@Override
public void onMessageReactionRemove(MessageReactionRemoveEvent e) pcall {
temp enter();
ret if !discordEnabled() || !licensed();
MessageReaction r = e.getReaction();
User user = e.getUser();
if (!reactToUser(user)) ret;
bool bot = user.isBot();
long msgID = r.getMessageIdLong();
S emoji = r.getReactionEmote().getName();
vmBus_send('discordReactionRemoved, litmapparams(
reaction := r,
fromBot := bot, +user, userID := user.getIdLong(),
module := dm_me(), +msgID, +emoji
));
}
@Override
public void onMessageReactionRemoveAll(MessageReactionRemoveAllEvent e) pcall {
temp enter();
print("TODO: onMessageReactionRemoveAll");
}
@Override
public void onGuildMemberJoin(GuildMemberJoinEvent event) pcall {
temp enter();
print("Got guild join");
ret if !discordEnabled() || !licensed();
print("Join >> getting user ID");
long userID = rcall_long getIdLong(rcall getUser(event.getMember()));
print("Join >> sending");
vmBus_send('discordGuildJoin, litmapparams(
module := dm_me(), +event, +userID, guildID := discord_guildIDFromEvent_gen(event)
));
}
@Override
public void onGuildMemberLeave(GuildMemberLeaveEvent event) pcall {
temp enter();
print("Got guild leave");
ret if !discordEnabled() || !licensed();
long userID = rcall_long getIdLong(rcall getUser(event.getMember()));
vmBus_send('discordGuildLeave, litmapparams(
module := dm_me(), +event, +userID
));
}
}, token := discordToken);
dm_registerAs('liveDiscordModule);
dm_vmBus_answerToMessage activeDiscordTokens(func -> LS { ll(discordToken) });
pcall {
setField(discordBotName := jda_selfUserName(discord));
setField(discordBotID := jda_selfUserID(discord));
}
print("Bot name: " + discordBotName);
}
void cleanMeUp {
if (discord != null) pcall {
print("Shutting down discord");
discord.shutdown();
print("Bot shut down");
}
discord = null;
}
O userConcept(User user) {
S crud = dm_gazelle_linesCRUD();
O userConcept = dm_call(crud, 'uniqUser, user.getIdLong());
dm_call(crud, 'cset, userConcept, litobjectarray(name := user.getName()));
ret userConcept;
}
// API
MessageChannel getChannel(long channelID) {
ret discord.getTextChannelById(channelID);
}
void postInChannel(long channelID, S msg) {
postInChannel(channelID, msg, null);
}
void postInChannel(long channelID, S msg, IVF1 onPost) {
if (channelID == 0) ret;
postInChannel(getChannel(channelID), msg, onPost);
}
void uploadFileInChannel(long channelID, File file, S fileName, S msg, IVF1 onPost) {
if (channelID == 0) ret;
uploadFileInChannel(getChannel(channelID), file, fileName, msg, onPost);
}
void postInChannel(MessageChannel channel, S msg, IVF1 onPost) {
S originalMsg = msg;
msg = shortenForDiscord(msg);
if (empty(msg)) ret;
S postID = !sendToPostedLinesCRUD ? null : (S) dm_pcall(gazelle_postedLinesCRUD(), 'postingLine, channel.getId(), msg);
print("Posting in channel " + channel + ": " + msg);
pcallFAll(onPostedInChannel, channel, msg);
MessageAction a = channel.sendMessage(msg);
if (msg != originalMsg)
a.addFile(toUtf8(originalMsg), genericTextFileName());
a.queue(m -> msgPosted(m, onPost, postID), onQueueError);
}
S genericTextFileName() {
ret ymd_minus_hms() + ".txt";
}
void msgPosted(Message m, IVF1 onPost, S postID) enter {
if (nempty(postID))
dm_call(gazelle_postedLinesCRUD(), 'donePosting, postID, "discord msg " + m.getId());
long msgId = m.getIdLong();
print("I sent msg: " + msgId);
pcallF(onPost, m);
}
void postInChannel(S channel, S msg) {
long id = dm_discord_channelID(channel);
if (id == 0) fail("Channel not found: " + channel);
postInChannel(id, msg);
}
void postInChannel(MessageChannel channel, S msg) {
postInChannel(channel, msg, null);
}
void postImage(Map msgMap, S url) {
postImage(msgMap, url, "");
}
void postImage(Map msgMap, S url, S description) {
postImage((MessageChannel) get channel(msgMap), url, description);
}
void postImage(MessageChannel channel, S url, S description) {
print("Posting image: " + url);
channel.sendMessage(
new EmbedBuilder()
.setImage(absoluteURL(url))
//.setTitle(unnull(title))
.setDescription(unnull(description))
.setColor(discordImageEmbedMysteriousLineColor)
.build()).queue(null, onQueueError); // TODO: posted lines
}
void editMessage(long channelID, long msgID, S text) {
getChannel(channelID).editMessageById(str(msgID), text).queue(null, onQueueError);
}
void sendPM(long userID, S _text) {
S text = shortenForDiscord(_text);
if (empty(text)) ret;
print("Sending PM to " + userID + ": " + text);
discord.getUserById(userID).openPrivateChannel().queue(channel -> {
channel.sendMessage(text).queue(null, onQueueError);
}, onQueueError);
print("PM queued");
}
void reply(Map msgMap, S text) {
reply(msgMap, text, null);
}
void reply(Map msgMap, S text, O onPost) {
if (empty(text = trim(text))) ret;
postInChannel((MessageChannel) msgMap.get('channel), text, toIVF1(onPost));
}
void iAmTyping(Map msgMap) pcall {
((MessageChannel) msgMap.get('channel)).sendTyping().queue(null, onQueueError);
}
S startKeepAliveModule() enter {
ret dm_discord_startKeepAliveModule(module());
}
bool reactToUser(User user) {
bool bot = user.isBot();
// don't react to other bots
if (bot && !reactToBots) false;
// don't read msgs from myself
if (user.getIdLong() == discordBotID) false;
true;
}
void uploadFileInChannel(MessageChannel channel, File file, S fileName, S msg, IVF1 onPost) {
msg = shortenForDiscord(msg);
MessageAction a = empty(msg)
? channel.sendFile(file, fileName)
: channel.sendMessage(msg).addFile(file, fileName);
a.queue(m -> msgPosted(m, onPost, null), onQueueError);
}
void uploadFileInChannel(MessageChannel channel, byte[] data, S fileName, S msg, IVF1 onPost) {
msg = shortenForDiscord(msg);
MessageAction a = empty(msg)
? channel.sendFile(data, fileName)
: channel.sendMessage(msg).addFile(data, fileName);
a.queue(m -> msgPosted(m, onPost, null), onQueueError);
}
void uploadFileInChannel(long channelID, byte[] data, S fileName, S msg, IVF1 onPost) {
uploadFileInChannel(getChannel(channelID), data, fileName, msg, onPost);
}
void queue(RestAction action) {
action.queue(null, onQueueError);
}