Warning: session_start(): open(/var/lib/php/sessions/sess_ahbs8qk9n1idaf3iu5lc0hqmq9, 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
!7
!include once #1026298 // DynTalkBot2 for JDA 4.0
set flag AllowMetaCode.
meta-transformNow { tok_cmdStatement }
standardBot1 RolesExporter {
init {
if (myName == null) setField(myName := "HyperCubes");
preprocessAtSelfToMyName = true;
escapeAtEveryone = true;
}
sync S processSimplifiedLine(S s, O... _) null {
try answer super.processSimplifiedLine(s, _);
optPar long channelID;
if (channelID == 0) null;
optPar Message msg;
Message.Attachment attachment = msg == null ? null : first(msg.getAttachments());
if (attachment != null) {
File file = downloadFileForChannel(channelID);
if (checkPerms(msg) != null)
file = appendToFileName(file, ".temp"); // don't save for further operations
print("Downloading attachment " + attachment.getFileName() + " to " + file);
deleteFile(file);
attachment.downloadToFile(file)
.exceptionally(error -> { temp enter(); print("download fail"); ret null with postInChannel(channelID, "Couldn't download attachment"); })
.thenAccept(file2 -> postInChannel(channelID, handleAttachment(file2)));
}
if null (s = dropMyPrefixOrNull(s)) null;
optPar MessageChannel channel;
try {
cmd "help" {
new EmbedBuilder eb;
eb.setColor(Color.yellow);
// "? ? ? ? ? ? ? ? ? ?"
eb.setAuthor(hexToUTF8("e2848d20e284bd20e2849920e284b020e2849b20e2848220e182ae20e284ac20e284b020d58f"), null, null);
eb.setTitle("Backup Your Discord Roles", null);
eb.setDescription("(This bot is still under development, many features will be added later on)");
S syntax = "...";
discordEmbed_addField(eb, "Syntax", syntax);
eb.setThumbnail(imageSnippetURL_noHttps(#1102865));
channel.sendMessage(eb.build()).queue();
null;
}
cmd "old help" ret trimAllLines([[
Upload a JSON file to channel to inspect/import it
@myName export - export roles + members as JSON
@myName rights - check bot's rights
@myName diff - diff last upload against server
MILDLY DANGEROUS @myName add roles - add missing roles from last upload
@myName unused roles - list roles not used in current server
DANGEROUS @myName delete unused roles - delete unused roles
@myName missing member roles - list roles that members should have according to last import
SOMEWHAT DANGEROUS @myName add member roles - add all roles mentioned in last line
]].replace("@myName", myName);
cmd "export"
ret uploadFileInChannel(channelID,
toUtf8(jsonEncode_breakAtNLevels(3, makeData())),
"members-and-roles-" + guild.getIdLong() + ".json.txt", null, null);
cmd "rights"
ret "Manage roles: " + yesNo(canManageRoles());
cmd "diff" {
File f = downloadFileForChannel(channelID);
if (!fileExists(f)) ret "No past upload found in this channel";
ret pairA(diff(f));
}
cmd "add roles" {
try answer checkPerms(msg);
Either p = diffWithLastUpload(channelID);
if (!isB(p)) ret eitherGetA(p);
Map diff = cast p.b();
Map rolesDiff = cast mapGet(diff, "roles");
print(+rolesDiff);
new LS buf;
for (JsonDiff.GatheredKey roleName, O roleDiff : unnull(rolesDiff)) {
//buf.add(roleName + ": " + className(roleDiff));
if (roleDiff cast JsonDiff.Added) {
//buf.add("Add role " + quote(roleName) + ": " + roleDiff);
//buf.add(className(roleDiff.b));
Map roleMap = cast roleDiff.b;
print(+roleMap);
S name = getString name(roleMap);
long permissions = getLong permissions(roleMap);
queue(guild.createRole()
.setName(name)
.setPermissions(permissions));
buf.add("Added role " + quote(roleName));
}
}
ret or2(lines(buf), "Nothing to do");
}
cmd "unused roles"
ret or2(lines(unusedRoles()), "No unused roles");
cmd "delete unused roles" {
try answer checkPerms(msg);
ret or2(mapToLines(unusedRoles(),
role -> { queue(role.delete()); ret "Deleted role: " + role.getName(); }),
"No unused roles");
}
cmd "missing member roles"
ret missingMemberRoles(false, _);
cmd "add member roles"
ret missingMemberRoles(true, _);
cmd "stats"
ret "Brains: " + l(dataByServer) + "\n"
+ "Persistent data size: " + (l(dm_moduleStruct()) + l(conceptsFile()));
} catch print e {
ret "Internal error: " + e;
}
}
S missingMemberRoles(bool doIt, O... _) {
optPar long channelID;
Either p = diffWithLastUpload(channelID);
if (!isB(p)) ret eitherGetA(p);
Map diff = cast p.b();
Map membersDiff = cast mapGet(diff, "members");
new LS buf;
for (JsonDiff.GatheredKey memberID, O memberDiff : unnull(membersDiff)) {
long _memberID = (long) memberID!;
Member member = guild.getMemberById(_memberID);
if (member == null) continue;
//buf.add(roleName + ": " + className(roleDiff));
if (memberDiff cast Map) {
Cl roleDiffs = values((Map) get roles(memberDiff));
for (O roleDiff : roleDiffs)
if (roleDiff cast JsonDiff.Added) {
S roleName = getString name(roleDiff.b);
buf.add(discordAt(_memberID) + " needs role " + roleName);
if (doIt) {
L roles = guild.getRolesByName(roleName, false);
if (empty(roles))
buf.add("Role " + quote(roleName) + " not found");
for (Role role : roles) {
queue(guild.addRoleToMember(member, role));
buf.add(" Added role " + role);
}
}
}
}
}
ret or2(lines(buf), "Nothing to do");
}
L unusedRoles() {
ret filter(guild.getRoles(), r -> empty(guild.getMembersWithRoles(r)));
}
S checkPerms(Message msg) {
if (!msg.getMember().hasPermission(Permission.MANAGE_ROLES))
ret "You have no permission to do that you sneaky boy";
null;
}
File downloadFileForChannel(long channelID) {
ret prepareCacheProgramFile(guildID + "/" + channelID);
}
bool canManageRoles() {
ret guild.getSelfMember().hasPermission(Permission.MANAGE_ROLES);
}
Map makeData() {
L roles = map(guild.getRoles(),
r -> litorderedmap(
"name" := r.getName(),
"id" := r.getIdLong(),
"permissions" := r.getPermissionsRaw()));
L members = map(guild.getMembers(),
m -> litorderedmap(
"nickName" := m.getNickname(),
"effectiveName" := m.getEffectiveName(),
"userName" := m.getUser().getName(),
"id" := m.getIdLong(),
"isOwner" := trueOrNull(m.isOwner()),
"roles" := map(m.getRoles(),
r -> litorderedmap("name" := r.getName(), "id" := r.getIdLong()))));
ret litorderedmap(
dataType := "members and roles for guild",
dateExported := dateWithSecondsGMT(),
guild := litorderedmap(
name := guild.getName(),
id := guild.getIdLong()),
+roles,
+members);
}
S handleAttachment(File file) enter {
try {
print("Handling attachment " + f2s(file));
ret pairA(diff(file));
} catch print e {
ret "Internal error";
}
}
// returns null if error
// else: result for user, diff
Pair diff(File file) {
O json;
try {
json = decodeJson(loadTextFile(file));
} catch e {
ret null with print("Not a JSON file: " + f2s(file));
}
fixImportedData(json);
new JsonDiff differ;
// identify roles by names and members by ID
differ.getListKey = path -> eq(last(path), 'roles) ? 'name : 'id;
O diff = differ.diff(makeData(), json);
//ret "Differences: " + structForUser(diff);
ret pair("Differences:\n" + nicelyFormatJsonDiff(diff), diff);
}
void fixImportedData(O data) {
changeKey((Map) data, 'allRoles, 'roles);
}
// either error or diff
Either diffWithLastUpload(long channelID) {
File f = downloadFileForChannel(channelID);
if (!fileExists(f)) ret eitherA("No past upload found in this channel");
Pair p = diff(f);
if (p == null) ret eitherA("There was an error with the last upload");
ret eitherB(p.b);
}
}