summaryrefslogtreecommitdiff
path: root/chatalias.py
blob: 67de106af6357a93ff9b9d1b40a5ad3d968741d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# TODO: Add cg/ac/msg support

import os
import mysqlhack
import org.bukkit as bukkit
from org.bukkit import *
from helpers import *


# Version number and requirements

alias_version = "2.1.0"
helpers_versions = ["1.1.0", "2.0.0"]
enabled = False
error_msg = colorify("&cUnspecified error")
commands_per_page = 5
global_aliases = {"./":"/"}
data = {}
use_mysql = True

# Permissions:

# Grants full access immediately
permission_ALL = "utils.alias.*"
# Access to the command to display the help screen
permission_BASE = "utils.alias"
# Make replacements only when the user has this permission
permission_USE = "utils.alias.use"
# Modify aliases
permission_MODIFY = "utils.alias.modify"
permission_MODIFY_OTHERS = "utils.alias.modify.others"
# List aliases
permission_LIST = "utils.alias.list"
permission_LIST_OTHERS = "utils.alias.list.others"
# Set alias amounts/length limits, e.g. utils.alias.amount.420
permission_AMOUNT = "utils.alias.amount."
default_alias_limit = 15
permission_LENGTH = "utils.alias.length."
default_length_limit = 120
# See when the plugin was disabled due to version errors
permission_INFO = "utils.alias.info"
permission_FINFO = "utils.alias.finfo"


def safe_open_json(uuid):
    if not os.path.exists("plugins/redstoner-utils.py.dir/files/aliases"):
        os.makedirs("plugins/redstoner-utils.py.dir/files/aliases")
    value = open_json_file("aliases/" + uuid)
    if value is None:
        value = dict(global_aliases)
        save_json_file("aliases/" + uuid, value)
    return value


def get_player_alias_limit(player):
    value = get_permission_content(player, permission_AMOUNT)
    if value is not None and value.isdigit():
        return int(value)
    return default_alias_limit


def get_player_length_limit(player):
    value = get_permission_content(player, permission_LENGTH)
    if value is not None and value.isdigit():
        return int(value)
    return default_length_limit


@hook.event("player.PlayerJoinEvent", "high")
def on_join(event):
    if enabled:
        t = threading.Thread(target=load_data, args=(uid(event.getPlayer()), ))
        t.daemon = True
        t.start()
    else:
        if event.getPlayer().hasPermission(permission_FINFO):
            disabled_fallback(event.getPlayer())


@hook.event("player.AsyncPlayerChatEvent", "high")
def on_player_chat(event):
    if enabled:
        if event.isCancelled():
            return
        player = event.getPlayer() 
        if not hasPerm(player, permission_USE):
            return
        msg_limit = get_player_length_limit(player)
        for alias, value in data[uid(player)].iteritems():
            if player.hasPermission("essentials.chat.color"):
                event.setMessage(event.getMessage().replace(colorify(alias), colorify(value)))
            else:
                event.setMessage(event.getMessage().replace(alias, value))
            if not player.hasPermission(permission_ALL) and len(event.getMessage()) > msg_limit:
                event.setCancelled(True)
                plugin_header(player, "Chatalias")
                msg(player, "The message you wanted to generate would exceed the length limit limit of %d. Please make it shorter!" % msg_limit)
                return


def hasPerm(player, permission):
    return (player.hasPermission(permission)) or (player.hasPermission(permission_ALL))


def disabled_fallback(receiver):
    if not hasPerm(receiver, permission_INFO):
        msg(receiver, colorify("&cUnknown command. Use &e/help&c, &e/plugins &cor ask a mod."))
    else:
        msg(receiver, colorify("&cPlugin alias v" + alias_version + " has experienced an &eEMERGENCY SHUTDOWN:"))
        msg(receiver, error_msg)
        msg(receiver, colorify("&cPlease contact a dev/admin (especially pep :P) about this to take a look at it."))


def can_remote(player):
    return hasPerm(player, permission_LIST_OTHERS) or hasPerm(player, permission_MODIFY_OTHERS)


# Command

@hook.command("alias",
              usage="/<command> <add, remove, list, help> [...]",
              desc="Allows aliasing of words")
def on_alias_command(sender, cmd, label, args):
    plugin_header(sender, "Chatalias")
    try:
        args = array_to_list(args)
        if not enabled:
            disabled_fallback(sender)
            return
        if not hasPerm(sender, permission_BASE):
            noperm(sender)
            return
        if args[0].lower() != "player" and not is_player(sender):
            msg(sender, "&cThe console cannot have aliases")
            return True
        subcommands[args[0].lower()](sender, args[1:])
    except:
        subcommands["help"](sender, "1")
    return True


def help(sender, args):
    commands = [colorify("&e/alias help [page]")]
    if hasPerm(sender, permission_LIST):
        commands += [colorify("&e/alias list &7- Lists all your aliases")]
    if hasPerm(sender, permission_MODIFY):
        commands += [colorify("&e/alias add <word> <alias> &7- Add an alias")]
        commands += [colorify("&e/alias remove <word> &7- Remove an alias")]
    if can_remote(sender):
        while len(commands) < commands_per_page:
            commands += [""]
        commands += [colorify("&7Following commands will be executed on <player> yet all output will be redirected to you, except when you set silent to false, then <player> will see it too.")]
    if hasPerm(sender, permission_LIST_OTHERS):
        commands += [colorify("&e/alias player <name> list [silent]")]
    if hasPerm(sender, permission_MODIFY_OTHERS):
        commands += [colorify("&e/alias player <name> add <word> <alias> [silent]")]
        commands += [colorify("&e/alias player <name> remove <word> [silent]")]
    pages = (len(commands)-1)/commands_per_page + 1
    page = 1
    if len(args) != 0:
        page = int(args[0])
    if (page > pages):
        page = pages
    if page < 1:
        page = 1
    msg(sender, colorify("&e---- &6Help &e-- &6Page &c" + str(page) + "&6/&c" + str(pages) + " &e----"))
    page -= 1
    to_display = commands[5*page:5*page+5]
    for message in to_display:
        msg(sender, message)
    if page+1 < pages:
        msg(sender, colorify("&6To display the next page, type &c/alias help " + str(page+2)))


def add(sender, args):
    uuid = uid(sender)
    args = [args[0]] + [" ".join(args[1:])]
    if (args[0] not in data[uuid]) and is_alias_limit_reached(sender, sender):
        return
    if not add_alias_data(uuid, args[0], args[1]):
        msg(sender, colorify("&c") + "Could not add this alias because it would cause some sequences to be replaced multiple times", usecolor = False)
        return
    msg(sender, colorify("&7Alias: ") + args[0] + colorify("&7 -> ") + args[1] + colorify("&7 was succesfully created!"), usecolor=sender.hasPermission("essentials.chat.color"))


def radd(sender, sender_name, target, args, silent):
    if len(args) < 2:
        msg(sender, "&cYou must pass a sequence and an alias for it")
        return
    replaced = args[0]
    alias = " ".join(args[1:])
    uuid = uid(target)
    if not silent:
        if sender is not target:
            plugin_header(target, "Chatalias")
        msg(target, "&cPlayer %s &cis creating an alias for you!" % sender_name)
    if (replaced not in data[uuid]) and is_alias_limit_reached(target, sender, silent):
        return
    if not add_alias_data(uuid, replaced, alias):
        message = colorify("&c") + "Could not add this alias because it would cause some sequences to be replaced multiple times"
        msg(sender, message, usecolor = False)
        if not silent:
            msg(target, message, usecolor = False)
        return
    message = colorify("&7Alias: &7%s&7 -> &7%s&7 was successfully created!") % ((colorify(replaced), colorify(alias)) if target.hasPermission("essentials.chat.color") else (replaced, alias))
    msg(sender, message, usecolor = False)
    if not silent:
        msg(target, message, usecolor = False)


def is_alias_limit_reached(player, recipient, silent = True):
    if player.hasPermission(permission_ALL):
        return False
    alias_limit = get_player_alias_limit(player)
    if len(data[uid(player)]) >= alias_limit:
        message = ("&cYour limit of %d has been reached" if player is recipient else "&cThe limit of %d has been reached for that player") % alias_limit
        msg(recipient, message)
        if not silent:
            msg(player, message)
        return True
    return False


def add_alias_data(puuid, aliased, new_alias):
    prior = dict(data[puuid])
    if aliased in prior:
        info("Checking prior, removing previous alias for " + aliased)
        del prior[aliased]

    # prevent 2 -> 3 if there is 1 -> 2
    for alias in prior.values():
        if aliased in alias:
            info("aliased %s in alias %s" % (aliased, alias))
            return False

    # prevent 1 -> 2 if there is 2 -> 3
    for sequence in prior:
        if sequence in new_alias:
            info("sequence %s in new_alias %s" % (sequence, new_alias))
            return False

    data[puuid][aliased] = new_alias
    save_data(puuid)
    return True


def remove(sender, args):
    try:
        msg(sender, colorify("&7Successfully removed alias ") + args[0] + colorify(" &7-> ") + data[uid(sender)].pop(args[0]) + colorify("&7!"), usecolor=sender.hasPermission("essentials.chat.color"))
        save_data(uid(sender))
    except:
        msg(sender, colorify("&cCould not remove alias ") + args[0] + colorify(", it does not exist."), usecolor=sender.hasPermission("essentials.chat.color"))


def rremove(sender, sender_name, target, args, silent):
    if len(args) < 1:
        msg(sender, "&cYou must specify a sequence whose alias is to be removed")
        return
    removed = args[0]
    uuid = uid(target)
    aliases = data[uuid]
    if not silent:
        msg(target, "&cPlayer %s &cis removing an alias for you!" % sender_name)
    if removed in aliases:
        alias = aliases.pop(removed)
        message = colorify("&7Alias: &7%s&7 -> &7%s&7 successfully removed!") % ((colorify(removed), colorify(alias)) if target.hasPermission("essentials.chat.color") else (removed, alias))
        msg(sender, message, usecolor = False)
        if not silent:
            msg(target, message, usecolor = False)
        save_data(uuid)
    else:
        message = colorify("&cCould not remove alias &7%s&c, it does not exist") % colorify(removed) if target.hasPermission("essentials.chat.color") else removed
        msg(sender, message, usecolor = False)
        if not silent:
            msg(target, message, usecolor = False)


def list_alias(sender, args):
    msg(sender, "&7You have a total of " + str(len(data[uid(sender)])) + " aliases:")
    for word, alias in data[str(uid(sender))].items():
        msg(sender, colorify("&7") + word + colorify("&7 -> ") + alias, usecolor=sender.hasPermission("essentials.chat.color"))


def rlist_alias(sender, sender_name, target, args, silent):
    aliases = data[uid(target)]
    msg(sender, "&7Player %s has a total of %d aliases:" % (target.getName(), len(aliases)))
    if not silent:
        if sender is not target:
            plugin_header(target, "Chatalias")
        msg(target, "&cPlayer %s &cis listing your aliases" % sender_name)
    if target.hasPermission("essentials.chat.color"):
        for pair in aliases.iteritems():
            msg(sender, colorify("&7%s&7 -> %s" % pair), usecolor = False)
    else:
        for pair in aliases.iteritems():
            msg(sender, colorify("&7%s&7 -> %s") % pair, usecolor = False)


def remote(sender, args):
    if len(args) < 2:
        msg(sender, "&cAlias remotes take at least 3 arguments")
        return
    target_remote = remotes.get(args[1].lower())
    if target_remote is None:
        msg(sender, "&cThat remote command does not exist")
        return
    target = server.getOfflinePlayer(args[0])
    if target is None or not (target.hasPlayedBefore() or target.isOnline()):
        msg(sender, "&cThat player could not be found")
        return
    silent = True
    if len(args) > (2 if target_remote is rlist_alias else 3 if target_remote is rremove else 4):
        if args[-1].lower() == "false":
            silent = sender is target or not target.isOnline()
            args = args[:-1]
        elif args[-1].lower() == "true":
            args = args[:-1]
    target_remote(sender, sender.getDisplayName() if is_player(sender) else colorify("&6Console"), target, args[2:], silent)


subcommands = {
    "help": help,
    "?": help,
    "add": add,
    "remove": remove,
    "del": remove,
    "delete": remove,
    "player": remote,
    "remote": remote,
    "list": list_alias
}

remotes = {
    "add": radd,
    "remove": rremove,
    "del": rremove,
    "delete": rremove,
    "list": rlist_alias,
}


# Storage
# MySQL Table:
# CREATE TABLE `chatalias` (`uuid` VARCHAR(36) PRIMARY KEY, `alias` TEXT);

def load_data(uuid):
    if use_mysql:
        try:
            t = threading.Thread(target=load_data_thread, args=(uuid,))
            t.daemon = True
            t.start()
        except:
            error(trace())
    else:
        data[uuid] = safe_open_json(uuid)

def load_data_thread(uuid):
    conn = zxJDBC.connect(mysql_database, mysql_user, mysql_pass, "com.mysql.jdbc.Driver")
    curs = conn.cursor()
    curs.execute("SELECT `alias` FROM `chatalias` WHERE `uuid` = ?;", (uuid, ))
    results = curs.fetchall()
    curs.close()
    conn.close()
    if len(results) == 0:
        value = dict(global_aliases)
    else:
        value = json_loads(results[0][0])
    data[uuid] = value


def save_data(uuid):
    if use_mysql:
        try:
            t = threading.Thread(target=save_data_thread, args=(uuid,))
            t.daemon = True
            t.start()
        except:
            error(trace())
    else:
        save_json_file("aliases/" + uuid, data[uuid])

def save_data_thread(uuid):
    conn = zxJDBC.connect(mysql_database, mysql_user, mysql_pass, "com.mysql.jdbc.Driver")
    curs = conn.cursor()
    curs.execute("INSERT INTO `chatalias` (`uuid`, `alias`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `alias` = VALUES(`alias`);", 
        (uuid, json_dumps(data[uuid])))
    conn.commit()
    curs.close()
    conn.close()


# OnModuleLoad

enabled = helpers_version in helpers_versions
if not enabled:
    error_msg = colorify("&6Incompatible versions detected (&chelpers.py&6)")
for player in server.getOnlinePlayers():
    if enabled:
        load_data(uid(player))
    else:
        if player.hasPermission(permission_FINFO):
            disabled_fallback(player)