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

232
LINES

< > BotCompany Repo | #1026316 // Discord Cloner v1 [saves and restores roles and members]

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

Uses 10819K of libraries. Click here for Pure Java version (15926L/85K).

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  
  init {
10  
    if (myName == null) setField(myName := "HyperCubes");
11  
    preprocessAtSelfToMyName = true;
12  
    escapeAtEveryone = true;
13  
  }
14  
  
15  
  sync S processSimplifiedLine(S s, O... _) null {
16  
    try answer super.processSimplifiedLine(s, _);
17  
    optPar long channelID;
18  
    if (channelID == 0) null;
19  
    optPar Message msg;
20  
    
21  
    Message.Attachment attachment = msg == null ? null : first(msg.getAttachments());
22  
    if (attachment != null) {
23  
      File file = downloadFileForChannel(channelID);
24  
      if (checkPerms(msg) != null)
25  
        file = appendToFileName(file, ".temp"); // don't save for further operations
26  
      print("Downloading attachment " + attachment.getFileName() + " to " + file);
27  
      deleteFile(file);
28  
      attachment.downloadToFile(file)
29  
        .exceptionally(error -> { temp enter(); print("download fail"); ret null with postInChannel(channelID, "Couldn't download attachment"); })
30  
        .thenAccept(file2 -> postInChannel(channelID, handleAttachment(file2)));
31  
    }
32  
    
33  
    if null (s = dropMyPrefixOrNull(s)) null;
34  
    
35  
    try {
36  
      cmd "help" ret trimAllLines([[
37  
        Upload a JSON file to channel to inspect/import it
38  
        
39  
        @myName export - export roles + members as JSON
40  
        @myName rights - check bot's rights
41  
        @myName diff - diff last upload against server
42  
        MILDLY DANGEROUS @myName add roles - add missing roles from last upload
43  
        @myName unused roles - list roles not used in current server
44  
        DANGEROUS @myName delete unused roles - delete unused roles
45  
        @myName missing member roles - list roles that members should have according to last import
46  
        SOMEWHAT DANGEROUS @myName add member roles - add all roles mentioned in last line
47  
        
48  
      ]].replace("@myName", myName);
49  
      
50  
      cmd "export"
51  
        ret uploadFileInChannel(channelID,
52  
          toUtf8(jsonEncode_breakAtNLevels(3, makeData())),
53  
          "members-and-roles-" + guild.getIdLong() + ".json.txt", null, null);
54  
          
55  
      cmd "rights"
56  
        ret "Manage roles: " + yesNo(canManageRoles());
57  
        
58  
      cmd "diff" {
59  
        File f = downloadFileForChannel(channelID);
60  
        if (!fileExists(f)) ret "No past upload found in this channel";
61  
        ret pairA(diff(f));
62  
      }
63  
      
64  
      cmd "add roles" {
65  
        try answer checkPerms(msg);
66  
        Either<S, O> p = diffWithLastUpload(channelID);
67  
        if (!isB(p)) ret eitherGetA(p);
68  
        Map diff = cast p.b();
69  
        Map rolesDiff = cast mapGet(diff, "roles");
70  
        print(+rolesDiff);
71  
        new LS buf;
72  
        for (JsonDiff.GatheredKey roleName, O roleDiff : unnull(rolesDiff)) {
73  
          //buf.add(roleName + ": " + className(roleDiff));
74  
          if (roleDiff cast JsonDiff.Added) {
75  
            //buf.add("Add role " + quote(roleName) + ": " + roleDiff);
76  
            //buf.add(className(roleDiff.b));
77  
            Map roleMap = cast roleDiff.b;
78  
            print(+roleMap);
79  
            S name = getString name(roleMap);
80  
            long permissions = getLong permissions(roleMap);
81  
            queue(guild.createRole()
82  
              .setName(name)
83  
              .setPermissions(permissions));
84  
            buf.add("Added role " + quote(roleName));
85  
          }
86  
        }
87  
        ret or2(lines(buf), "Nothing to do");
88  
      }
89  
      
90  
      cmd "unused roles"
91  
        ret or2(lines(unusedRoles()), "No unused roles");
92  
        
93  
      cmd "delete unused roles" {
94  
        try answer checkPerms(msg);
95  
        ret or2(mapToLines(unusedRoles(),
96  
          role -> { queue(role.delete()); ret "Deleted role: " + role.getName(); }),
97  
          "No unused roles");
98  
      }
99  
      
100  
      cmd "missing member roles"
101  
        ret missingMemberRoles(false, _);
102  
        
103  
      cmd "add member roles"
104  
        ret missingMemberRoles(true, _);
105  
106  
    } catch print e {
107  
      ret "Internal error: " + e;
108  
    }
109  
  }
110  
  
111  
  S missingMemberRoles(bool doIt, O... _) {
112  
    optPar long channelID;
113  
    
114  
    Either<S, O> p = diffWithLastUpload(channelID);
115  
    if (!isB(p)) ret eitherGetA(p);
116  
    Map diff = cast p.b();
117  
    Map membersDiff = cast mapGet(diff, "members");
118  
    new LS buf;
119  
    for (JsonDiff.GatheredKey memberID, O memberDiff : unnull(membersDiff)) {
120  
      long _memberID = (long) memberID!;
121  
      Member member = guild.getMemberById(_memberID);
122  
      if (member == null) continue;
123  
      //buf.add(roleName + ": " + className(roleDiff));
124  
      if (memberDiff cast Map) {
125  
        Cl roleDiffs = values((Map) get roles(memberDiff));
126  
        for (O roleDiff : roleDiffs)
127  
          if (roleDiff cast JsonDiff.Added) {
128  
            S roleName = getString name(roleDiff.b);
129  
            buf.add(discordAt(_memberID) + " needs role " + roleName);
130  
            if (doIt) {
131  
              L<Role> roles = guild.getRolesByName(roleName, false);
132  
              if (empty(roles))
133  
                buf.add("Role " + quote(roleName) + " not found");
134  
              for (Role role : roles) {
135  
                queue(guild.addRoleToMember(member, role));
136  
                buf.add("  Added role " + role);
137  
              }
138  
            }
139  
          }
140  
      }
141  
    }
142  
    ret or2(lines(buf), "Nothing to do");
143  
  }
144  
  
145  
  L<Role> unusedRoles() {
146  
    ret filter(guild.getRoles(), r -> empty(guild.getMembersWithRoles(r)));
147  
  }
148  
  
149  
  S checkPerms(Message msg) {
150  
    if (!msg.getMember().hasPermission(Permission.MANAGE_ROLES))
151  
      ret "You have no permission to do that you sneaky boy";
152  
    null;
153  
  }
154  
  
155  
  File downloadFileForChannel(long channelID) {
156  
    ret prepareCacheProgramFile(guildID + "/" + channelID);
157  
  }
158  
  
159  
  bool canManageRoles() {
160  
    ret guild.getSelfMember().hasPermission(Permission.MANAGE_ROLES);
161  
  }
162  
163  
  Map makeData() {  
164  
    L roles = map(guild.getRoles(),
165  
      r -> litorderedmap(
166  
        "name" := r.getName(),
167  
        "id" := r.getIdLong(),
168  
        "permissions" := r.getPermissionsRaw()));
169  
      
170  
    L members = map(guild.getMembers(),
171  
      m -> litorderedmap(
172  
        "nickName" := m.getNickname(),
173  
        "effectiveName" := m.getEffectiveName(),
174  
        "userName" := m.getUser().getName(),
175  
        "id" := m.getIdLong(),
176  
        "isOwner" := trueOrNull(m.isOwner()),
177  
        "roles" := map(m.getRoles(),
178  
          r -> litorderedmap("name" := r.getName(), "id" := r.getIdLong()))));
179  
        
180  
    ret litorderedmap(
181  
      dataType := "members and roles for guild",
182  
      dateExported := dateWithSecondsGMT(),
183  
      guild := litorderedmap(
184  
        name := guild.getName(),
185  
        id := guild.getIdLong()),
186  
      +roles,
187  
      +members);
188  
  }
189  
  
190  
  S handleAttachment(File file) enter {
191  
    try {
192  
      print("Handling attachment " + f2s(file));
193  
      ret pairA(diff(file));
194  
    } catch print e {
195  
      ret "Internal error";
196  
    }
197  
  }
198  
199  
  // returns null if error
200  
  // else: result for user, diff
201  
  Pair<S, O> diff(File file) {  
202  
    O json;
203  
    try {
204  
      json = decodeJson(loadTextFile(file));
205  
    } catch e {
206  
      ret null with print("Not a JSON file: " + f2s(file));
207  
    }
208  
    
209  
    fixImportedData(json);
210  
    
211  
    new JsonDiff differ;
212  
    // identify roles by names and members by ID
213  
    differ.getListKey = path -> eq(last(path), 'roles) ? 'name : 'id;
214  
    O diff = differ.diff(makeData(), json);
215  
    
216  
    //ret "Differences: " + structForUser(diff);
217  
    ret pair("Differences:\n" + nicelyFormatJsonDiff(diff), diff);
218  
  }
219  
  
220  
  void fixImportedData(O data) {
221  
    changeKey((Map) data, 'allRoles, 'roles);
222  
  }
223  
  
224  
  // either error or diff
225  
  Either<S, O> diffWithLastUpload(long channelID) {
226  
    File f = downloadFileForChannel(channelID);
227  
    if (!fileExists(f)) ret eitherA("No past upload found in this channel");
228  
    Pair<S, O> p = diff(f);
229  
    if (p == null) ret eitherA("There was an error with the last upload");
230  
    ret eitherB(p.b);
231  
  }
232  
}

Author comment

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: 225 / 318
Version history: 3 change(s)
Referenced in: [show references]