My small project I'm working on is meant to be a League of Legends 3rd party application ran along side it to preemptively warn the user before they go about making the incorrect decision to trash talk or bm their teammates which I think is one of the lasting major problems with this decade old game. It consists of two main parts which are a chat monitor that does the checking of the input and attempts to quickly alert the user with a sound warning and a listener that tries to mimic the in game chat as well as it can.

Mostly what my concern is with this project was how I'm sharing data between the threads and if it is an effective method or if I should worry at all about such things on this small scale of a project. The lists on the surface seemed like the best way to deal with an unknown length of characters that seemed to function as a type of "globalish" data structure that could contain characters, but it feels slightly hacky for some reason. I'm aware of locks/semaphores and how they work and what their purpose generally is but I also can't tell if its necessary for the data that I am altering in this program.

It begins by creating the variables that are for the most part sharable by the two threads and then joining these threads. Most of these are meant to work like boolean values which are always set and not incremented or decremented so I didn't think that locks would be entirely necessary but I could be very wrong. As far as I am aware lists in python were seemingly global and self locking so I'm unsure if anything needed to be done with them specifically to make things more smooth or not cause race conditions.

if __name__ == '__main__':
    currChat = []  # current chat session
    lastChecked = []  # keeps track of last checked series of characters
    inProg = multiprocessing.Value('i', 1)  # used to terminate program manually
    inChat = multiprocessing.Value('i', 0)  # toggles chat mode when in program
    checkWait = multiprocessing.Value('i', 1)  # allows profanity check to wait until user has stopped input for a
    # few seconds before checking

    t1 = Thread(target=keyboard_listener)
    t2 = Thread(target=chat_monitor)

    print('Process terminated')

The first of the two threads being the keyboard listener where the on_press is where most of the functionality is. The goal of this portion is to both attempt to accurately mimic what information would be happening within the in game chat, including all of the ways to leave/cancel the chat session temporarily as well as delay the chat monitor slightly as to not waste resources checking the chat before the text has been completely input.

def on_press(key):
    # if not in chat the enter key will begin the chat session
    if inChat.value == 0:
        if key == keyboard.Key.enter:
            inChat.value = 1
            print('Not in chat')
            pass  # when not in chat keystrokes are ignored
        if key == keyboard.Key.esc:
            inChat.value = 0
            # record alphanumeric and space characters for profanity check thread
            if key == keyboard.Key.space:
                currChat.append(' ')
            elif key == keyboard.Key.backspace:
                except IndexError:
                    print('No characters in string')
            # also alert user if they enter the profane text
            elif key == keyboard.Key.enter:
                enterString = ""
                for ch in currChat:
                    enterString += ch
                if profanity.contains_profanity(enterString):
                    print('You have entered something foul')
                    winsound.Beep(300, 700)t
                    print('You are reformed')

                inChat.value = 0

                except AttributeError:
                    print('Special character {0} entered'.format(key))
        checkWait.value = 1


The only functionality in my on_release listener is to manually flip the inProg flag which kills the chat_monitor and other listener and is arbitrarily there and not in on_press for more readability. It is not entirely necessary but the on_release function would be needed to run this program the way it currently is set up.

The chat monitor waits for user input to halt, waits 1.5 seconds and converts the list of characters into a string that can be run through a function from the profanity package. It checks this string and copies the currChat list into the lastChecked list. This is done to avoid repeated checks and also avoids repeated alerting about the same issue.

def chat_monitor():
    print('Chat monitor has started')

    # wait some time after keyboard input and then check for profanity
    while inProg:
        if checkWait.value == 1:
            checkWait.value = 0
            # if the string is the same as when it was last checked we won't waste time checking again
            if currChat and currChat != lastChecked:
                checkString = ""
                for ch in currChat:
                    checkString += ch

                if profanity.contains_profanity(checkString):
                    winsound.Beep(300, 700)
                    print('Think about the consequences')
                    print('So far so good')

            lastChecked = currChat[:]  # Copies elements of currChat into lastChecked
            checkWait.value = 1

    print('Chat monitor terminated')

To end this I think I'll mention things that are either future goals or issues that do not take away from the original functional goal of the program.

There is a known issue here where the profanity filter only considers single words to be profane. As an example "fuck" is considered profane but "fuckfuck" is not. This is an inconsistency that hopefully could be addressed with one of my stretch goals of creating my own profanity filter.

I thought of a particular security issue with this program working like a temporary keylogger and wanted to put in a good faith effort to counteract it but haven't started and assume it will be more complicated than what I've done thus far. The main focus of the player while a game of league is open will be the game but some players might tab out or leave the active game and in rare cases input sensitive information. The player however would likely have to be in chat. This would get picked up by the program if the player was in chat and somehow tabbed out through a way I was unaware of.

These were my stretch goals that I temporarily removed to focus on the functional aspects of the project. If you made it this far and have any ideas on how to effectively take on any of the first 4 of my TODOs mentioned below, any thoughts would be welcome.

    # Stretch Goals
    # TODO: Launch process when League of Legends is started.  
    # TODO: Begin process when game client is detected.
    # TODO: Build my own profanity filter(Search trie/prefix tree).

    # TODO: Since this tracks keystrokes I also should find a way to only track when game 
    # is actively open.  In rare cases someone could enter sensitive info while still
    # in game while tabbed.  This is to attempt to avoid malicious hijacking of the software.
    # May not be needed but still seems useful. Possibly warn user if I can't figure this out
    # on launch to avoid this behavior.

    # TODO/Fluff: Web GUI component. Database log of past misconduct.
  • \$\begingroup\$ Thanks I looked at it and attempted to change several parts that didn't meet this criteria. One thing I'm curious about is whether the way I have broken down the code and explanations is sufficient or should I have all of the explanation of the project separate from the code itself. It seemed to flow better if I could explain it piece by piece but I can attempt to reform the question if needed. \$\endgroup\$ Jul 21 at 1:10


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.