Uses 10819K of libraries. Click here for Pure Java version (15926L/85K).
!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; try { cmd "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<S, O> 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, _); } catch print e { ret "Internal error: " + e; } } S missingMemberRoles(bool doIt, O... _) { optPar long channelID; Either<S, O> 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<Role> 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<Role> 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<S, O> 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<S, O> diffWithLastUpload(long channelID) { File f = downloadFileForChannel(channelID); if (!fileExists(f)) ret eitherA("No past upload found in this channel"); Pair<S, O> p = diff(f); if (p == null) ret eitherA("There was an error with the last upload"); ret eitherB(p.b); } }
Began life as a copy of #1026297
download show line numbers debug dex old transpilations
Travelled to 6 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1026316 |
Snippet name: | Discord Cloner v1 [saves and restores roles and members] |
Eternal ID of this version: | #1026316/4 |
Text MD5: | 15cdf74fd3e9ae9918a76a8d9f59f556 |
Transpilation MD5: | 1a3cda2d3f338e3a8d3aea1c4933f7b5 |
Author: | stefan |
Category: | javax / discord |
Type: | JavaX source code (Dynamic Module) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2019-12-15 22:59:57 |
Source code size: | 8042 bytes / 232 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 224 / 317 |
Version history: | 3 change(s) |
Referenced in: | #1026415 - Tomii Boi Discord Bot [now works everywhere, dev.] |