Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

349
LINES

< > BotCompany Repo | #1026297 - Discord Cloner ["Hypercubes Bot", LIVE]

JavaX source code (Dynamic Module) [tags: use-pretranspiled] - run with: Stefan's OS

Uses 10702K of libraries. Click here for Pure Java version (16565L/90K).

1  
!7
2  
3  
!include once #1026298 // DynTalkBot2 for JDA 4.0
4  
5  
set flag AllowMetaCode.
6  
meta-transformNow { tok_cmdStatement }
7  
8  
standardBot1 RolesExporter {
9  
  allServers {
10  
    transient S discordInvite = "https://discord.gg/RXqf97d";
11  
    transient S status = discordInvite;
12  
  }
13  
  
14  
  init {
15  
    print("All permissions: " + Permission.ALL_PERMISSIONS);
16  
    if (myName == null) setField(myName := "HyperCubes");
17  
    preprocessAtSelfToMyName = true;
18  
    escapeAtEveryone = true;
19  
    dropPunctuation = false;
20  
21  
    onDiscordStarted(r {
22  
      doEveryAndNow(20.0, r { gazelle_discord_playingGame(status) });
23  
    });
24  
  }
25  
  
26  
  sync S processSimplifiedLine(S s, O... _) null {
27  
    try answer super.processSimplifiedLine(s, _);
28  
    optPar long channelID;
29  
    if (channelID == 0) null;
30  
    optPar Message msg;
31  
    
32  
    Message.Attachment attachment = msg == null ? null : first(msg.getAttachments());
33  
    if (attachment != null) {
34  
      File file = appendToFileName(downloadFileForChannel(channelID), "." + now());
35  
      print("Downloading attachment " + attachment.getFileName() + " to " + file);
36  
      bool authed = checkPerms(msg) == null;
37  
      deleteFile(file);
38  
      attachment.downloadToFile(file)
39  
        .exceptionally(error -> { temp enter(); print("download fail"); ret null with postInChannel(channelID, "Couldn't download attachment"); })
40  
        .thenAccept(file2 -> postInChannel(channelID, handleAttachment(file2, channelID, authed)));
41  
    }
42  
    
43  
    optPar MessageChannel channel;
44  
    
45  
    S s1 = s;
46  
    if ((s = dropMyPrefixOrNull(s)) != null) try {
47  
      cmd "help" { 
48  
        new EmbedBuilder eb;
49  
        eb.setColor(toColor("F14527"));
50  
        
51  
        eb.setAuthor(fancyName(), null, null);
52  
        
53  
        eb.setTitle("Backup Your Discord Roles", null);
54  
        eb.setDescription([[
55  
In next update: Manage roles, full layout + roles backup
56  
57  
(This bot is still under development, many features will be added later on.)
58  
59  
Type `HyperCubes howto` for a short tutorial.
60  
61  
If you like to support this project, join us at <https://discord.gg/JyCEwmb> and consider donating. We thank you in advance
62  
63  
If you are interested to make your own smart bot, visit <https://BotCompany.de>
64  
]]);
65  
66  
        discordEmbed_addField(eb, "Upload", "Upload a JSON file to channel to inspect/import it");
67  
68  
        S syntax = [[
69  
Export roles + members as JSON
70  
`  HyperCubes export                [e]`
71  
Check bot's rights
72  
`  HyperCubes rights                [r]`
73  
74  
Diff last upload against server
75  
`  HyperCubes compare               [c]`
76  
Add missing roles from last upload
77  
`  HyperCubes add roles            [ar]`
78  
List roles that members should have according to last import
79  
`  HyperCubes list missing  roles [lmr]`
80  
Add all roles mentioned in last line
81  
`  HyperCubes add member roles    [amr]`
82  
83  
List roles not used in current server
84  
`  HyperCubes unused roles         [ur]`
85  
Delete unused roles
86  
`  HyperCubes delete unused roles [dur]`
87  
88  
Invite bot to other server
89  
`  HyperCubes invite                   `
90  
]];
91  
        
92  
        discordEmbed_addField(eb, "Syntax (abbreviations in brackets)", syntax);
93  
        
94  
        eb.setThumbnail(imageSnippetURL_noHttps(#1102865));
95  
        channel.sendMessage(eb.build()).queue();
96  
        null;
97  
      }
98  
99  
      cmd "howto|how to|how-to|tutorial" {
100  
        new EmbedBuilder eb;
101  
        eb.setColor(toColor("F14527"));
102  
        
103  
        eb.setAuthor(fancyName(), null, null);
104  
        eb.setTitle(" ", null);
105  
        
106  
        eb.setDescription([[
107  
**How to copy roles from an old server to a new one**
108  
109  
In the old server, type `HyperCubes export` which gives you a JSON file.
110  
111  
In a channel in the new server:
112  
*Simply upload the JSON file as an attachment. You will see a list of all differences in members+roles
113  
*Type `HyperCubes lmr` to see missing roles
114  
*Type `HyperCubes amr` to create missing roles and assign them to appropriate members        
115  
      ]]);
116  
        channel.sendMessage(eb.build()).queue();
117  
        null;
118  
      }
119  
      
120  
      cmd "export|e"
121  
        ret uploadFileInChannel(channelID,
122  
          toUtf8(jsonEncode_breakAtNLevels(3, makeData())),
123  
          "members-and-roles-" + guild.getIdLong() + ".json.txt", null, null);
124  
          
125  
      cmd "rights|r"
126  
        ret canManageRoles()
127  
          ? "I have the right to manage roles. I have everything I want!"
128  
          : "I am not authorized to manage roles, so I can only preview changes, not perform them.";
129  
        
130  
      cmd "compare|c" {
131  
        File f = downloadFileForChannel(channelID);
132  
        if (!fileExists(f)) ret "No past upload found in this channel";
133  
        ret pairA(diff(f));
134  
      }
135  
      
136  
      cmd "add roles|ar" 
137  
        ret or2(addRoles(_), "Nothing to do");
138  
      
139  
      cmd "unused roles|ur"
140  
        ret or2(lines(unusedRoles()), "No unused roles");
141  
        
142  
      cmd "delete unused roles|dur" {
143  
        try answer checkPerms(msg);
144  
        ret or2(mapToLines(unusedRoles(),
145  
          role -> { queue(role.delete()); ret "Deleted role: " + role.getName(); }),
146  
          "No unused roles");
147  
      }
148  
      
149  
      cmd "list missing roles|lmr"
150  
        ret or2(missingMemberRoles(false, _), "Nothing to do");
151  
        
152  
      cmd "add member roles|amr"
153  
 {
154  
        try answer checkPerms(msg);
155  
        S a = addRoles(_);
156  
        ret or2(a + missingMemberRoles(true, _), "Nothing to do");
157  
      }
158  
        
159  
      cmd "stats"
160  
        ret "Brains: " + l(dataByServer) + "\n"
161  
        + "Persistent data size: " + (l(dm_moduleStruct()) + l(conceptsFile()));
162  
163  
      cmd "invite"
164  
        ret "Invite me to a server: <" + discordBotInviteLink(discordBotID, discord_adminPermission()) + ">\n" +
165  
          "Join my Discord: <" + discordInvite + ">";
166  
167  
    } catch print e {
168  
      ret "Internal error: " + e;
169  
    }
170  
    
171  
    try answer webBot_answer(s1, #1026544);
172  
  }
173  
  
174  
  S missingMemberRoles(bool doIt, O... _) {
175  
    optPar long channelID;
176  
    
177  
    Either<S, O> p = diffWithLastUpload(channelID);
178  
    if (!isB(p)) ret eitherGetA(p);
179  
    Map diff = cast p.b();
180  
    Map membersDiff = cast mapGet(diff, "members");
181  
    new LS buf;
182  
    for (JsonDiff.GatheredKey memberID, O memberDiff : unnull(membersDiff)) {
183  
      long _memberID = (long) memberID!;
184  
      Member member = guild.getMemberById(_memberID);
185  
      if (member == null) continue;
186  
      //buf.add(roleName + ": " + className(roleDiff));
187  
      if (memberDiff cast Map) {
188  
        Cl roleDiffs = values((Map) get roles(memberDiff));
189  
        for (O roleDiff : roleDiffs)
190  
          if (roleDiff cast JsonDiff.Added) {
191  
            S roleName = getString name(roleDiff.b);
192  
            buf.add(discordAt(_memberID) + " needs role " + roleName);
193  
            if (doIt) {
194  
              L<Role> roles = guild.getRolesByName(roleName, false);
195  
              if (empty(roles))
196  
                buf.add("Role " + quote(roleName) + " not found");
197  
              for (Role role : roles) {
198  
                queue(guild.addRoleToMember(member, role));
199  
                buf.add("  Added role " + role);
200  
              }
201  
            }
202  
          }
203  
      }
204  
    }
205  
    ret lines(buf);
206  
  }
207  
  
208  
  L<Role> unusedRoles() {
209  
    ret filter(guild.getRoles(), r -> empty(guild.getMembersWithRoles(r)));
210  
  }
211  
  
212  
  S checkPerms(Message msg) {
213  
    if (!msg.getMember().hasPermission(Permission.MANAGE_ROLES))
214  
      ret "You have no permission to do that you sneaky boy";
215  
    null;
216  
  }
217  
  
218  
  File downloadFileForChannel(long channelID) {
219  
    ret prepareCacheProgramFile(guildID + "/" + channelID);
220  
  }
221  
  
222  
  bool canManageRoles() {
223  
    ret guild.getSelfMember().hasPermission(Permission.MANAGE_ROLES);
224  
  }
225  
226  
  Map makeData() {  
227  
    L roles = map(guild.getRoles(),
228  
      r -> litorderedmap(
229  
        "name" := r.getName(),
230  
        "id" := r.getIdLong(),
231  
        "permissions" := r.getPermissionsRaw()));
232  
      
233  
    L members = map(guild.getMembers(),
234  
      m -> litorderedmap(
235  
        "nickName" := m.getNickname(),
236  
        "effectiveName" := m.getEffectiveName(),
237  
        "userName" := m.getUser().getName(),
238  
        "id" := m.getIdLong(),
239  
        "isOwner" := trueOrNull(m.isOwner()),
240  
        "roles" := map(m.getRoles(),
241  
          r -> litorderedmap("name" := r.getName(), "id" := r.getIdLong()))));
242  
        
243  
    ret litorderedmap(
244  
      dataType := "members and roles for guild",
245  
      dateExported := dateWithSecondsGMT(),
246  
      guild := litorderedmap(
247  
        name := guild.getName(),
248  
        id := guild.getIdLong()),
249  
      +roles,
250  
      +members);
251  
  }
252  
  
253  
  S handleAttachment(File file, long channelID, bool authed) enter {
254  
    try {
255  
      print("Handling attachment " + f2s(file));
256  
257  
      O json;
258  
      try {
259  
        json = decodeJson(loadTextFile(file));
260  
      } catch e {
261  
        ret null with print("Not a JSON file: " + f2s(file));
262  
      }
263  
264  
      if (!isMyKindOfData(json)) null;
265  
266  
      S a = "";
267  
      if (authed) {
268  
        copyFile(file, downloadFileForChannel(channelID));
269  
        a = "Saving upload\n";
270  
      }
271  
      
272  
      ret a + unnull(pairA(diff(file)));
273  
    } catch print e {
274  
      ret "Internal error";
275  
    }
276  
  }
277  
278  
  // returns null if error
279  
  // else: result for user, diff
280  
  Pair<S, O> diff(File file) {  
281  
    O json;
282  
    try {
283  
      json = decodeJson(loadTextFile(file));
284  
    } catch e {
285  
      ret null with print("Not a JSON file: " + f2s(file));
286  
    }
287  
    
288  
    fixImportedData(json);
289  
290  
    new JsonDiff differ;
291  
    // identify roles by names and members by ID
292  
    differ.getListKey = path -> eq(last(path), 'roles) ? 'name : 'id;
293  
    O diff = differ.diff(makeData(), json);
294  
    
295  
    //ret "Differences: " + structForUser(diff);
296  
    ret pair("Differences:\n" + nicelyFormatJsonDiff(diff), diff);
297  
  }
298  
  
299  
  void fixImportedData(O data) {
300  
    changeKey((Map) data, 'allRoles, 'roles);
301  
  }
302  
303  
  bool isMyKindOfData(O data) {
304  
    ret isMap(data) && containsOneKeyOf((Map) data, "roles", "allRoles");
305  
  }
306  
  
307  
  // either error or diff
308  
  Either<S, O> diffWithLastUpload(long channelID) {
309  
    File f = downloadFileForChannel(channelID);
310  
    if (!fileExists(f)) ret eitherA("No past upload found in this channel");
311  
    Pair<S, O> p = diff(f);
312  
    if (p == null) ret eitherA("There was an error with the last upload");
313  
    ret eitherB(p.b);
314  
  }
315  
316  
  S addRoles(O... _) {
317  
    optPar Message msg;
318  
    optPar long channelID;
319  
    try answer checkPerms(msg);
320  
    Either<S, O> p = diffWithLastUpload(channelID);
321  
    if (!isB(p)) ret eitherGetA(p);
322  
    Map diff = cast p.b();
323  
    Map rolesDiff = cast mapGet(diff, "roles");
324  
    print(+rolesDiff);
325  
    new LS buf;
326  
    for (JsonDiff.GatheredKey roleName, O roleDiff : unnull(rolesDiff)) {
327  
      //buf.add(roleName + ": " + className(roleDiff));
328  
      if (roleDiff cast JsonDiff.Added) {
329  
        //buf.add("Add role " + quote(roleName) + ": " + roleDiff);
330  
        //buf.add(className(roleDiff.b));
331  
        Map roleMap = cast roleDiff.b;
332  
        print(+roleMap);
333  
        S name = getString name(roleMap);
334  
        long permissions = getLong permissions(roleMap);
335  
        guild.createRole()
336  
          .setName(name)
337  
          .setPermissions(permissions & Permission.ALL_PERMISSIONS) // clear undefined bits
338  
          .complete(); // further actions may depend on this, so block until done
339  
        buf.add("Added role " + quote(name));
340  
      }
341  
    }
342  
    ret lines(buf);
343  
  }
344  
345  
  // HYPERCUBES in fancy font
346  
  S fancyName() {
347  
    ret hexToUTF8("e2848d20e284bd20e2849920e284b020e2849b20e2848220e182ae20e284ac20e284b020d58f");
348  
  }
349  
}

Author comment

Began life as a copy of #1025767

download  show line numbers  debug dex   

Travelled to 4 computer(s): mqqgnosmbjvj, onxytkatvevr, tvejysmllsmz, xrpafgyirdlv

No comments. add comment

Snippet ID: #1026297
Snippet name: Discord Cloner ["Hypercubes Bot", LIVE]
Eternal ID of this version: #1026297/156
Text MD5: 81ad5c7347810874fdeaaeab94350545
Transpilation MD5: 5da2baaab5ee60f0b729612f9c99c2ad
Author: stefan
Category: javax / discord
Type: JavaX source code (Dynamic Module)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2020-01-19 01:06:59
Source code size: 11601 bytes / 349 lines
Pitched / IR pitched: No / No
Views / Downloads: 343 / 4330
Version history: 155 change(s)
Referenced in: [show references]

Formerly at http://tinybrain.de/1026297 & http://1026297.tinybrain.de