Uses 11217K of libraries. Click here for Pure Java version (20089L/107K).
!7 !include once #1026298 // DynTalkBot2 for JDA 4.0 set flag AllowMetaCode. meta-transformNow { tok_cmdStatement } standardBot1 RolesExporter { bool qaEnabled = true; // disable if guild is spammy allServers { transient S discordInvite = "https://discord.gg/RXqf97d"; transient S status = discordInvite; } init { print("All permissions: " + Permission.ALL_PERMISSIONS); if (myName == null) setField(myName := "HyperCubes"); preprocessAtSelfToMyName = true; escapeAtEveryone = true; dropPunctuation = false; onDiscordStarted(r { doEveryAndNow(20.0, r { gazelle_discord_playingGame(status) }); }); } 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 = appendToFileName(downloadFileForChannel(channelID), "." + now()); print("Downloading attachment " + attachment.getFileName() + " to " + file); bool authed = checkPerms(msg) == null; 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, channelID, authed))); } optPar MessageChannel channel; S s1 = s; if ((s = dropMyPrefixOrNull(s)) != null) try { cmd "help" { new EmbedBuilder eb; eb.setColor(toColor("F14527")); eb.setAuthor(fancyName(), null, null); eb.setTitle("Backup Your Discord Roles", null); eb.setDescription([[ In next update: Manage roles, full layout + roles backup (This bot is still under development, many features will be added later on.) Type `HyperCubes howto` for a short tutorial. If you like to support this project, join us at <https://discord.gg/JyCEwmb> and consider donating. We thank you in advance If you are interested to make your own smart bot, visit <https://BotCompany.de> ]]); discordEmbed_addField(eb, "Upload", "Upload a JSON file to channel to inspect/import it"); S syntax = [[ Export roles + members as JSON ` HyperCubes export [e]` Check bot's rights ` HyperCubes rights [r]` Diff last upload against server ` HyperCubes compare [c]` Add missing roles from last upload ` HyperCubes add roles [ar]` List roles that members should have according to last import ` HyperCubes list missing roles [lmr]` Add all roles mentioned in last line ` HyperCubes add member roles [amr]` List roles not used in current server ` HyperCubes unused roles [ur]` Delete unused roles ` HyperCubes delete unused roles [dur]` Invite bot to other server ` HyperCubes invite ` ]]; discordEmbed_addField(eb, "Syntax (abbreviations in brackets)", syntax); eb.setThumbnail(imageSnippetURL_noHttps(#1102865)); channel.sendMessage(eb.build()).queue(); null; } cmd "howto|how to|how-to|tutorial" { new EmbedBuilder eb; eb.setColor(toColor("F14527")); eb.setAuthor(fancyName(), null, null); eb.setTitle(" ", null); eb.setDescription([[ **How to copy roles from an old server to a new one** In the old server, type `HyperCubes export` which gives you a JSON file. In a channel in the new server: *Simply upload the JSON file as an attachment. You will see a list of all differences in members+roles *Type `HyperCubes lmr` to see missing roles *Type `HyperCubes amr` to create missing roles and assign them to appropriate members ]]); channel.sendMessage(eb.build()).queue(); null; } cmd "export|e" ret uploadFileInChannel(channelID, toUtf8(jsonEncode_breakAtNLevels(3, makeData())), "members-and-roles-" + guild.getIdLong() + ".json.txt", null, null); cmd "rights|r" ret canManageRoles() ? "I have the right to manage roles. I have everything I want!" : "I am not authorized to manage roles, so I can only preview changes, not perform them."; cmd "compare|c" { File f = downloadFileForChannel(channelID); if (!fileExists(f)) ret "No past upload found in this channel"; ret pairA(diff(f)); } cmd "add roles|ar" ret or2(addRoles(_), "Nothing to do"); cmd "unused roles|ur" ret or2(lines(unusedRoles()), "No unused roles"); cmd "delete unused roles|dur" { try answer checkPerms(msg); ret or2(mapToLines(unusedRoles(), role -> { queue(role.delete()); ret "Deleted role: " + role.getName(); }), "No unused roles"); } cmd "list missing roles|lmr" ret or2(missingMemberRoles(false, _), "Nothing to do"); cmd "add member roles|amr" { try answer checkPerms(msg); S a = addRoles(_); ret or2(a + missingMemberRoles(true, _), "Nothing to do"); } cmd "stats" ret "Brains: " + l(dataByServer) + "\n" + "Persistent data size: " + (l(dm_moduleStruct()) + l(conceptsFile())); cmd "invite" ret "Invite me to a server: <" + discordBotInviteLink(discordBotID, discord_adminPermission()) + ">\n" + "Join my Discord: <" + discordInvite + ">"; } catch print e { ret "Internal error: " + e; } if (qaEnabled) try answer webBot_answer(s1, #1026544); } 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<JsonDiff.GatheredKey, O> 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 lines(buf); } 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, long channelID, bool authed) enter { try { print("Handling attachment " + f2s(file)); O json; try { json = decodeJson(loadTextFile(file)); } catch e { ret null with print("Not a JSON file: " + f2s(file)); } if (!isMyKindOfData(json)) null; S a = ""; if (authed) { copyFile(file, downloadFileForChannel(channelID)); a = "Saving upload\n"; } ret a + unnull(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); } bool isMyKindOfData(O data) { ret isMap(data) && containsOneKeyOf((Map) data, "roles", "allRoles"); } // 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); } S addRoles(O... _) { optPar Message msg; optPar long channelID; try answer checkPerms(msg); Either<S, O> p = diffWithLastUpload(channelID); if (!isB(p)) ret eitherGetA(p); Map diff = cast p.b(); Map<JsonDiff.GatheredKey, O> 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); guild.createRole() .setName(name) .setPermissions(permissions & Permission.ALL_PERMISSIONS) // clear undefined bits .complete(); // further actions may depend on this, so block until done buf.add("Added role " + quote(name)); } } ret lines(buf); } // HYPERCUBES in fancy font S fancyName() { ret hexToUTF8("e2848d20e284bd20e2849920e284b020e2849b20e2848220e182ae20e284ac20e284b020d58f"); } }
Began life as a copy of #1025767
download show line numbers debug dex old transpilations
Travelled to 8 computer(s): bhatertpkbcr, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv
No comments. add comment
Snippet ID: | #1026297 |
Snippet name: | Discord Cloner ["Hypercubes Bot", LIVE] |
Eternal ID of this version: | #1026297/163 |
Text MD5: | 519794b2d4f9696c6f790d18f95cfc70 |
Transpilation MD5: | f28532a9e8080681f680a4730558c410 |
Author: | stefan |
Category: | javax / discord |
Type: | JavaX source code (Dynamic Module) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2021-10-28 23:04:57 |
Source code size: | 11733 bytes / 352 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 709 / 92195 |
Version history: | 162 change(s) |
Referenced in: | [show references] |