<commit_before>#!/usr/bin/env python

###
# Copyright (c) 2002, Jeremiah Fincher
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright notice,
#     this list of conditions, and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright notice,
#     this list of conditions, and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#   * Neither the name of the author of this software nor the name of
#     contributors to this software may be used to endorse or promote products
#     derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###

"""
A simple module to handle various informational commands querying the bot's
current status and statistics.
"""
import plugins

import os
import sys
import sets
import time
import threading
from itertools import islice, ifilter, imap

import conf
import utils
import world
import privmsgs
import callbacks

def configure(onStart, afterConnect, advanced):
    # This will be called by setup.py to configure this module.  onStart and
    # afterConnect are both lists.  Append to onStart the commands you would
    # like to be run when the bot is started; append to afterConnect the
    # commands you would like to be run when the bot has finished connecting.
    from questions import expect, anything, something, yn
    onStart.append('load Status')

class UptimeDB(object):
    def __init__(self, filename='uptimes'):
        self.filename = os.path.join(conf.dataDir, filename)
        if os.path.exists(self.filename):
            fd = file(self.filename)
            s = fd.read()
            fd.close()
            s = s.replace('\n', ' ')
            self.uptimes = eval(s)
        else:
            self.uptimes = []

    def die(self):
        fd = file(self.filename, 'w')
        fd.write(repr(self.top(50)))
        fd.write('\n')
        fd.close()

    def add(self):
        if not any(lambda t: t[0] == world.startedAt, self.uptimes):
            self.uptimes.append((world.startedAt, None))

    def top(self, n=3):
        def decorator(t):
            if t[1] is None:
                return 0.0
            else:
                t[1] - t[0]
        def invertCmp(cmp):
            def f(x, y):
                return -cmp(x, y)
            return f
        def notNone(t):
            return t[1] is not None
        utils.sortBy(decorator, self.uptimes, cmp=invertCmp(cmp))
        return list(islice(ifilter(notNone, self.uptimes), 3))

    def update(self):
        for (i, t) in enumerate(self.uptimes):
            if t[0] == world.startedAt:
                self.uptimes[i] = (t[0], time.time())

class Status(callbacks.Privmsg):
    def __init__(self):
        callbacks.Privmsg.__init__(self)
        self.sentMsgs = 0
        self.recvdMsgs = 0
        self.sentBytes = 0
        self.recvdBytes = 0
        self.uptimes = UptimeDB()
        self.uptimes.add()
        self.uptimes.update()

    def inFilter(self, irc, msg):
        self.uptimes.update()
        self.recvdMsgs += 1
        self.recvdBytes += len(msg)
        return msg

    def outFilter(self, irc, msg):
        self.sentMsgs += 1
        self.sentBytes += len(msg)
        return msg

    def die(self):
        self.uptimes.update()
        self.uptimes.die()

    def bestuptime(self, irc, msg, args):
        """takes no arguments

        Returns the highest uptimes attained by the bot.
        """
        L = self.uptimes.top()
        if not L:
            irc.error(msg, 'I don\'t have enough data to answer that.')
            return
        def format((started, ended)):
            return '%s until %s; up for %s' % \
                   (time.strftime(conf.humanTimestampFormat,
                                  time.localtime(started)),
                    time.strftime(conf.humanTimestampFormat,
                                  time.localtime(ended)),
                    utils.timeElapsed(ended-started))
        irc.reply(msg, utils.commaAndify(imap(format, L)))

    def netstats(self, irc, msg, args):
        """takes no arguments

        Returns some interesting network-related statistics.
        """
        irc.reply(msg,
                   'I have received %s messages for a total of %s bytes.  '\
                   'I have sent %s messages for a total of %s bytes.' %\
                   (self.recvdMsgs, self.recvdBytes,
                    self.sentMsgs, self.sentBytes))

    def cpustats(self, irc, msg, args):
        """takes no arguments

        Returns some interesting CPU-related statistics on the bot.
        """
        (user, system, childUser, childSystem, elapsed) = os.times()
        now = time.time()
        timeRunning = now - world.startedAt
        if user+system > timeRunning:
            irc.error(msg, 'I seem to be running on a platform without an '
                           'accurate way of determining how long I\'ve been '
                           'running.')
            return
        activeThreads = threading.activeCount()
        response = ('I have taken %.2f seconds of user time and %.2f seconds '
                    'of system time, for a total of %.2f seconds of CPU '
                    'time.  My children have taken %.2f seconds of user time '
                    'and %.2f seconds of system time for a total of %.2f '
                    'seconds of CPU time.  Out of %s I have %s active.' %
                    (user, system, user + system,
                     childUser, childSystem, childUser + childSystem,
                     utils.nItems(world.threadsSpawned, 'thread', 'spawned'),
                     activeThreads))
        mem = None
        pid = os.getpid()
        plat = sys.platform
        try:
            if plat.startswith('linux') or plat.startswith('sunos') or \
               plat.startswith('freebsd') or plat.startswith('openbsd'):
                r = os.popen('ps -o vsz -p %s' % pid)
                r.readline() # VSZ Header.
                mem = int(r.readline().strip())
                r.close()
            elif sys.platform.startswith('netbsd'):
                mem = os.stat('/proc/%s/mem')[7]
            response += '  I\'m taking up %s kB of memory.' % mem
        except Exception, e:
            debug.msg(debug.exnToString(e))
        irc.reply(msg, response)

    def cmdstats(self, irc, msg, args):
        """takes no arguments

        Returns some interesting command-related statistics.
        """
        commands = 0
        callbacksPrivmsg = 0
        for cb in irc.callbacks:
            if isinstance(cb, callbacks.Privmsg) and cb.public:
                if not isinstance(cb, callbacks.PrivmsgRegexp):
                    callbacksPrivmsg += 1
                for attr in dir(cb):
                    if cb.isCommand(attr) and \
                       attr == callbacks.canonicalName(attr):
                        commands += 1
        s = 'I offer a total of %s in %s.  I have processed %s.' % \
            (utils.nItems(commands, 'command'),
             utils.nItems(callbacksPrivmsg, 'plugin', 'command-based'),
             utils.nItems(world.commandsProcessed, 'command'))
        irc.reply(msg, s)

    def commands(self, irc, msg, args):
        """takes no arguments

        Returns a list of the commands offered by the bot.
        """
        commands = sets.Set()
        for cb in irc.callbacks:
            if isinstance(cb, callbacks.Privmsg) and \
               not isinstance(cb, callbacks.PrivmsgRegexp) and cb.public:
                for attr in dir(cb):
                    if cb.isCommand(attr) and \
                       attr == callbacks.canonicalName(attr):
                        commands.add(attr)
        commands = list(commands)
        commands.sort()
        irc.reply(msg, utils.commaAndify(commands))

    def uptime(self, irc, msg, args):
        """takes no arguments.

        Returns the amount of time the bot has been running.
        """
        response = 'I have been running for %s.' % \
                   utils.timeElapsed(time.time() - world.startedAt)
        irc.reply(msg, response)

Class = Status

# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
<commit_msg>Poor attempt to fix the 33 years bug.<commit_after>#!/usr/bin/env python

###
# Copyright (c) 2002, Jeremiah Fincher
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   * Redistributions of source code must retain the above copyright notice,
#     this list of conditions, and the following disclaimer.
#   * Redistributions in binary form must reproduce the above copyright notice,
#     this list of conditions, and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#   * Neither the name of the author of this software nor the name of
#     contributors to this software may be used to endorse or promote products
#     derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###

"""
A simple module to handle various informational commands querying the bot's
current status and statistics.
"""
import plugins

import os
import sys
import sets
import time
import threading
from itertools import islice, ifilter, imap

import conf
import utils
import world
import privmsgs
import callbacks

def configure(onStart, afterConnect, advanced):
    # This will be called by setup.py to configure this module.  onStart and
    # afterConnect are both lists.  Append to onStart the commands you would
    # like to be run when the bot is started; append to afterConnect the
    # commands you would like to be run when the bot has finished connecting.
    from questions import expect, anything, something, yn
    onStart.append('load Status')

class UptimeDB(object):
    def __init__(self, filename='uptimes'):
        self.filename = os.path.join(conf.dataDir, filename)
        if os.path.exists(self.filename):
            fd = file(self.filename)
            s = fd.read()
            fd.close()
            s = s.replace('\n', ' ')
            self.uptimes = eval(s)
        else:
            self.uptimes = []

    def die(self):
        fd = file(self.filename, 'w')
        fd.write(repr(self.top(50)))
        fd.write('\n')
        fd.close()

    def add(self):
        if world.startedAt != 0 and \
           not any(lambda t: t[0] == world.startedAt, self.uptimes):
            self.uptimes.append((world.startedAt, None))

    def top(self, n=3):
        def decorator(t):
            if t[1] is None:
                return 0.0
            else:
                t[1] - t[0]
        def invertCmp(cmp):
            def f(x, y):
                return -cmp(x, y)
            return f
        def notNone(t):
            return t[1] is not None
        utils.sortBy(decorator, self.uptimes, cmp=invertCmp(cmp))
        return list(islice(ifilter(notNone, self.uptimes), n))

    def update(self):
        for (i, t) in enumerate(self.uptimes):
            if t[0] == world.startedAt:
                self.uptimes[i] = (t[0], time.time())

class Status(callbacks.Privmsg):
    def __init__(self):
        callbacks.Privmsg.__init__(self)
        self.sentMsgs = 0
        self.recvdMsgs = 0
        self.sentBytes = 0
        self.recvdBytes = 0
        self.uptimes = UptimeDB()
        self.uptimes.add()
        self.uptimes.update()

    def inFilter(self, irc, msg):
        self.uptimes.update()
        self.recvdMsgs += 1
        self.recvdBytes += len(msg)
        return msg

    def outFilter(self, irc, msg):
        self.sentMsgs += 1
        self.sentBytes += len(msg)
        return msg

    def die(self):
        self.uptimes.update()
        self.uptimes.die()

    def bestuptime(self, irc, msg, args):
        """takes no arguments

        Returns the highest uptimes attained by the bot.
        """
        L = self.uptimes.top()
        if not L:
            irc.error(msg, 'I don\'t have enough data to answer that.')
            return
        def format((started, ended)):
            return '%s until %s; up for %s' % \
                   (time.strftime(conf.humanTimestampFormat,
                                  time.localtime(started)),
                    time.strftime(conf.humanTimestampFormat,
                                  time.localtime(ended)),
                    utils.timeElapsed(ended-started))
        irc.reply(msg, utils.commaAndify(imap(format, L)))

    def netstats(self, irc, msg, args):
        """takes no arguments

        Returns some interesting network-related statistics.
        """
        irc.reply(msg,
                   'I have received %s messages for a total of %s bytes.  '\
                   'I have sent %s messages for a total of %s bytes.' %\
                   (self.recvdMsgs, self.recvdBytes,
                    self.sentMsgs, self.sentBytes))

    def cpustats(self, irc, msg, args):
        """takes no arguments

        Returns some interesting CPU-related statistics on the bot.
        """
        (user, system, childUser, childSystem, elapsed) = os.times()
        now = time.time()
        timeRunning = now - world.startedAt
        if user+system > timeRunning:
            irc.error(msg, 'I seem to be running on a platform without an '
                           'accurate way of determining how long I\'ve been '
                           'running.')
            return
        activeThreads = threading.activeCount()
        response = ('I have taken %.2f seconds of user time and %.2f seconds '
                    'of system time, for a total of %.2f seconds of CPU '
                    'time.  My children have taken %.2f seconds of user time '
                    'and %.2f seconds of system time for a total of %.2f '
                    'seconds of CPU time.  Out of %s I have %s active.' %
                    (user, system, user + system,
                     childUser, childSystem, childUser + childSystem,
                     utils.nItems(world.threadsSpawned, 'thread', 'spawned'),
                     activeThreads))
        mem = None
        pid = os.getpid()
        plat = sys.platform
        try:
            if plat.startswith('linux') or plat.startswith('sunos') or \
               plat.startswith('freebsd') or plat.startswith('openbsd'):
                r = os.popen('ps -o vsz -p %s' % pid)
                r.readline() # VSZ Header.
                mem = int(r.readline().strip())
                r.close()
            elif sys.platform.startswith('netbsd'):
                mem = os.stat('/proc/%s/mem')[7]
            response += '  I\'m taking up %s kB of memory.' % mem
        except Exception, e:
            debug.msg(debug.exnToString(e))
        irc.reply(msg, response)

    def cmdstats(self, irc, msg, args):
        """takes no arguments

        Returns some interesting command-related statistics.
        """
        commands = 0
        callbacksPrivmsg = 0
        for cb in irc.callbacks:
            if isinstance(cb, callbacks.Privmsg) and cb.public:
                if not isinstance(cb, callbacks.PrivmsgRegexp):
                    callbacksPrivmsg += 1
                for attr in dir(cb):
                    if cb.isCommand(attr) and \
                       attr == callbacks.canonicalName(attr):
                        commands += 1
        s = 'I offer a total of %s in %s.  I have processed %s.' % \
            (utils.nItems(commands, 'command'),
             utils.nItems(callbacksPrivmsg, 'plugin', 'command-based'),
             utils.nItems(world.commandsProcessed, 'command'))
        irc.reply(msg, s)

    def commands(self, irc, msg, args):
        """takes no arguments

        Returns a list of the commands offered by the bot.
        """
        commands = sets.Set()
        for cb in irc.callbacks:
            if isinstance(cb, callbacks.Privmsg) and \
               not isinstance(cb, callbacks.PrivmsgRegexp) and cb.public:
                for attr in dir(cb):
                    if cb.isCommand(attr) and \
                       attr == callbacks.canonicalName(attr):
                        commands.add(attr)
        commands = list(commands)
        commands.sort()
        irc.reply(msg, utils.commaAndify(commands))

    def uptime(self, irc, msg, args):
        """takes no arguments.

        Returns the amount of time the bot has been running.
        """
        response = 'I have been running for %s.' % \
                   utils.timeElapsed(time.time() - world.startedAt)
        irc.reply(msg, response)

Class = Status

# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
<|endoftext|>


<commit_before>from flask_restful import current_app

from zou.app.models.task_type import TaskType
from zou.app.models.task_status import TaskStatus
from zou.app.models.project import Project
from zou.app.models.person import Person
from zou.app.models.task import Task

from zou.app.services import deletion_service, tasks_service

from zou.app.blueprints.source.shotgun.base import (
    BaseImportShotgunResource,
    ImportRemoveShotgunBaseResource,
)

class ImportShotgunTasksResource(BaseImportShotgunResource):
    def prepare_import(self):
        self.project_ids = Project.get_id_map()
        self.person_ids = Person.get_id_map()
        self.task_type_ids = TaskType.get_id_map(field="name")
        self.task_status_ids = TaskStatus.get_id_map(field="short_name")

    def filtered_entries(self):
        return [x for x in self.sg_entries if self.is_valid_task(x)]

    def is_valid_task(self, sg_task):
        return sg_task["step"] is not None and sg_task["project"] is not None

    def extract_data(self, sg_task):
        entity_id = self.get_entity_id(sg_task["entity"])
        task_status_id = self.task_status_ids.get(
            sg_task["sg_status_list"], None
        )
        step_name = sg_task["step"]["name"]
        assigner_id = self.person_ids.get(sg_task["created_by"]["id"], None)
        project_id = self.project_ids.get(sg_task["project"]["id"], None)
        task_type_id = self.task_type_ids.get(step_name, None)
        assignees = self.extract_assignees(sg_task, self.person_ids)

        return {
            "name": sg_task["cached_display_name"],
            "shotgun_id": sg_task["id"],
            "start_date": sg_task["start_date"],
            "due_date": sg_task["due_date"],
            "sort_order": sg_task["sg_sort_order"],
            "duration": sg_task["duration"],
            "task_type_id": task_type_id,
            "task_status_id": task_status_id,
            "project_id": project_id,
            "entity_id": entity_id,
            "assigner_id": assigner_id,
            "assignees": assignees,
        }

    def get_entity_id(self, sg_entity):
        entity_id = None
        if sg_entity is not None:
            entity_sg_id = sg_entity["id"]
            entity_type = sg_entity["type"]
            if entity_type == "Asset":
                entity_id = self.get_asset_id(entity_sg_id)
            elif entity_type == "Shot":
                entity_id = self.get_shot_id(entity_sg_id)
            elif entity_type == "Scene":
                entity_id = self.get_scene_id(entity_sg_id)
            elif entity_type == "Sequence":
                entity_id = self.get_sequence_id(entity_sg_id)
            elif entity_type == "Episode":
                entity_id = self.get_episode_id(entity_sg_id)
            else:
                return None

        return entity_id

    def extract_assignees(self, sg_task, person_ids):
        assignees = []
        if len(sg_task["task_assignees"]) > 0:
            for sg_person in sg_task["task_assignees"]:
                person_id = person_ids[sg_person["id"]]
                person = Person.get(person_id)
                assignees.append(person)
        return assignees

    def import_entry(self, data):
        task = Task.get_by(shotgun_id=data["shotgun_id"])

        if task is None:
            task = Task.get_by(
                name=data["name"],
                project_id=data["project_id"],
                task_type_id=data["task_type_id"],
                entity_id=data["entity_id"],
            )

        if task is None:
            task = Task(**data)
            task.save()
            current_app.logger.info("Task created: %s" % task)
        else:
            existing_task = Task.get_by(
                name=data["name"],
                project_id=data["project_id"],
                task_type_id=data["task_type_id"],
                entity_id=data["entity_id"],
            )

            if existing_task is not None:
                data.pop("name", None)
                data.pop("project_id", None)
                data.pop("task_type_id", None)
                data.pop("entity_id", None)

            task.update(data)
            tasks_service.clear_task_cache(str(task.id))
            current_app.logger.info("Task updated: %s" % task)

        return task

class ImportRemoveShotgunTaskResource(ImportRemoveShotgunBaseResource):
    def __init__(self):
        ImportRemoveShotgunBaseResource.__init__(self, Task, self.delete_func)

    def delete_func(self, entity):
        deletion_service.remove_task(entity.id)
        tasks_service.clear_task_cache(str(entity.id))
<commit_msg>Fix data extraction for Shotgun tasks<commit_after>from flask_restful import current_app

from zou.app.models.task_type import TaskType
from zou.app.models.task_status import TaskStatus
from zou.app.models.project import Project
from zou.app.models.person import Person
from zou.app.models.task import Task

from zou.app.services import deletion_service, tasks_service

from zou.app.blueprints.source.shotgun.base import (
    BaseImportShotgunResource,
    ImportRemoveShotgunBaseResource,
)

class ImportShotgunTasksResource(BaseImportShotgunResource):
    def prepare_import(self):
        self.project_ids = Project.get_id_map()
        self.person_ids = Person.get_id_map()
        self.task_type_ids = TaskType.get_id_map(field="shotgun_id")
        self.task_status_ids = TaskStatus.get_id_map(field="short_name")

    def filtered_entries(self):
        return [x for x in self.sg_entries if self.is_valid_task(x)]

    def is_valid_task(self, sg_task):
        return sg_task["step"] is not None and sg_task["project"] is not None

    def extract_data(self, sg_task):
        entity_id = self.get_entity_id(sg_task["entity"])
        task_status_id = self.task_status_ids.get(
            sg_task["sg_status_list"], None
        )
        step_shotgun_id = sg_task["step"]["id"]
        assigner_id = self.person_ids.get(sg_task["created_by"]["id"], None)
        project_id = self.project_ids.get(sg_task["project"]["id"], None)
        task_type_id = self.task_type_ids.get(step_shotgun_id, None)
        assignees = self.extract_assignees(sg_task, self.person_ids)

        return {
            "name": sg_task["cached_display_name"],
            "shotgun_id": sg_task["id"],
            "start_date": sg_task["start_date"],
            "due_date": sg_task["due_date"],
            "sort_order": sg_task["sg_sort_order"],
            "duration": sg_task["duration"],
            "task_type_id": task_type_id,
            "task_status_id": task_status_id,
            "project_id": project_id,
            "entity_id": entity_id,
            "assigner_id": assigner_id,
            "assignees": assignees,
        }

    def get_entity_id(self, sg_entity):
        entity_id = None
        if sg_entity is not None:
            entity_sg_id = sg_entity["id"]
            entity_type = sg_entity["type"]
            if entity_type == "Asset":
                entity_id = self.get_asset_id(entity_sg_id)
            elif entity_type == "Shot":
                entity_id = self.get_shot_id(entity_sg_id)
            elif entity_type == "Scene":
                entity_id = self.get_scene_id(entity_sg_id)
            elif entity_type == "Sequence":
                entity_id = self.get_sequence_id(entity_sg_id)
            elif entity_type == "Episode":
                entity_id = self.get_episode_id(entity_sg_id)
            else:
                return None

        return entity_id

    def extract_assignees(self, sg_task, person_ids):
        assignees = []
        if len(sg_task["task_assignees"]) > 0:
            for sg_person in sg_task["task_assignees"]:
                person_id = person_ids[sg_person["id"]]
                person = Person.get(person_id)
                assignees.append(person)
        return assignees

    def import_entry(self, data):
        task = Task.get_by(shotgun_id=data["shotgun_id"])

        if task is None:
            task = Task.get_by(
                name=data["name"],
                project_id=data["project_id"],
                task_type_id=data["task_type_id"],
                entity_id=data["entity_id"],
            )

        if task is None:
            task = Task(**data)
            task.save()
            current_app.logger.info("Task created: %s" % task)
        else:
            existing_task = Task.get_by(
                name=data["name"],
                project_id=data["project_id"],
                task_type_id=data["task_type_id"],
                entity_id=data["entity_id"],
            )

            if existing_task is not None:
                data.pop("name", None)
                data.pop("project_id", None)
                data.pop("task_type_id", None)
                data.pop("entity_id", None)

            task.update(data)
            tasks_service.clear_task_cache(str(task.id))
            current_app.logger.info("Task updated: %s" % task)

        return task

class ImportRemoveShotgunTaskResource(ImportRemoveShotgunBaseResource):
    def __init__(self):
        ImportRemoveShotgunBaseResource.__init__(self, Task, self.delete_func)

    def delete_func(self, entity):
        deletion_service.remove_task(entity.id)
        tasks_service.clear_task_cache(str(entity.id))
<|endoftext|>


<commit_before>from util import hook
import random

@hook.command
def author(inp,nick = None):
	".author <Author Name> -- Will return details regarding the author"
	authpages = []
	totalrating = 0
	taletotal = 0
	scptotal = 0
	goitotal = 0
	pagerating = 0
	author = inp
	multimatch = []
	authorpage = ""
	found = 0
	exact = 0
	rewrite =0
	orgauth = ""
	newauth = ""
	rewriteauthor =0
	pagetitle = ""
	try:
		for page in scppages:
			if "scp" in taglist[page] or "tale" in taglist[page] or "goi-format" in taglist[page]: #makes sure only articles are counted
				if ":rewrite:" in authorlist[page]:
					bothauths = authorlist[page].split(":rewrite:")
					orgauth = bothauths[0]
					newauth = bothauths[1]
					if author == newauth:
						rewriteauthor = 1
				if author == authorlist[page] or rewriteauthor ==1:
					found =1
					rewriteauthor = 0

					authpages.append(page)
					pagetitle = titlelist[page]
					pagerating = ratinglist[page]
					totalrating = totalrating + pagerating
					if "scp" in taglist[page]:
						scptotal +=1
					if  "tale" in taglist[page]:
						taletotal+=1
					if  "goi-format" in taglist[page]:
						goitotal+=1
				try:
					if inp.lower() in authorlist[page].lower(): #this just matches the author with the first author match
						if inp.lower() == authorlist[page].lower():
							exact +=1
						if exact == 1:
							author = authorlist[page]
							authpages = []
							multimatch = [authorlist[page]]
						elif exact < 1:
							multimatch.append(authorlist[page])
						if inp.lower() in authorlist[page].lower() and found == 0:
							author = authorlist[page]
							if ":rewrite:" in authorlist[page]:
								bothauths = authorlist[page].split(":rewrite:")
								orgauth = bothauths[0]
								newauth = bothauths[1]
								if inp.lower() in orgauth.lower():
									author = orgauth
								if inp.lower() in newauth.lower():
									author = newauth
								rewrite = 1
							found = 1
							authpages.append(page)
							pagetitle = titlelist[page]
							pagerating = ratinglist[page]
							totalrating = totalrating + pagerating
							if "scp" in taglist[page]:
								scptotal +=1
							if  "tale" in taglist[page]:
								taletotal+=1
							if  "goi-format" in taglist[page]:
								goitotal+=1
				except AttributeError:
					pass
			else:
				if "author" in taglist[page]:
					if ":rewrite:" in authorlist[page]:
						bothauths = authorlist[page].split(":rewrite:")
						orgauth = bothauths[0]
						newauth = bothauths[1]
						if newauth == author:
							authorpage = "http://scp-wiki.net/"+page+" - "
					if author == authorlist[page]:
						authorpage = "http://scp-wiki.net/"+page+" - "
	except KeyError:
		pass
	plusauth = []
	moreauthors = 1
	plusauth.append(author)
	for authors in multimatch: #checks to see if multiple authors found
		z =0
		if ":rewrite:" in authors:
			continue
		for foundauthor in plusauth:
			if foundauthor ==authors:
				z =1
		if authors != author:
			if z == 0:
				moreauthors +=1
				plusauth.append(authors)
	if moreauthors>1:
		x = 0
		final = "Did you mean "
		for auth in plusauth:
			x+=1
			if x ==1:
				final+=auth+""
			if x ==2 and moreauthors ==2:
				final+=" or "+auth+"?"
			if x==2 and moreauthors >2:
				final+=", "+auth+""
			if x==3 and moreauthors ==3:
				final += ", or "+auth+"?"
			if x==3 and moreauthors >3:
				final += ", or "+auth+"? With " + str(moreauthors) + " more authors matching your query."
		return final
	avgrating = 0
	if taletotal+scptotal+goitotal is not 0: #just so no division by zero
		avgrating = totalrating/(taletotal+scptotal+goitotal)
	if not authpages: #if no author pages are added
		return "Author not found."
	final = authorpage+""+author +" has written " + str(scptotal) + " SCPs, "+str(taletotal)+" tales, and. "+str(goitotal)+" GOI formats. They have " + str(totalrating)+ " net upvotes with an average rating of " + str(avgrating) + ". Their most recent article is " + pagetitle + "(Rating:" + str(pagerating) + ")"
	return final
<commit_msg>Added .ad<commit_after>from util import hook
import random

@hook.command
def author(inp,nick = None):
	".author <Author Name> -- Will return details regarding the author"
	authpages = []
	totalrating = 0
	taletotal = 0
	scptotal = 0
	goitotal = 0
	pagerating = 0
	author = inp
	multimatch = []
	authorpage = ""
	found = 0
	exact = 0
	rewrite =0
	orgauth = ""
	newauth = ""
	rewriteauthor =0
	pagetitle = ""
	try:
		for page in scppages:
			if "scp" in taglist[page] or "tale" in taglist[page] or "goi-format" in taglist[page]: #makes sure only articles are counted
				if ":rewrite:" in authorlist[page]:
					bothauths = authorlist[page].split(":rewrite:")
					orgauth = bothauths[0]
					newauth = bothauths[1]
					if author == newauth:
						rewriteauthor = 1
				if author == authorlist[page] or rewriteauthor ==1:
					found =1
					rewriteauthor = 0

					authpages.append(page)
					pagetitle = titlelist[page]
					pagerating = ratinglist[page]
					totalrating = totalrating + pagerating
					if "scp" in taglist[page]:
						scptotal +=1
					if  "tale" in taglist[page]:
						taletotal+=1
					if  "goi-format" in taglist[page]:
						goitotal+=1
				try:
					if inp.lower() in authorlist[page].lower(): #this just matches the author with the first author match
						if inp.lower() == authorlist[page].lower():
							exact +=1
						if exact == 1:
							author = authorlist[page]
							authpages = []
							multimatch = [authorlist[page]]
						elif exact < 1:
							multimatch.append(authorlist[page])
						if inp.lower() in authorlist[page].lower() and found == 0:
							author = authorlist[page]
							if ":rewrite:" in authorlist[page]:
								bothauths = authorlist[page].split(":rewrite:")
								orgauth = bothauths[0]
								newauth = bothauths[1]
								if inp.lower() in orgauth.lower():
									author = orgauth
								if inp.lower() in newauth.lower():
									author = newauth
								rewrite = 1
							found = 1
							authpages.append(page)
							pagetitle = titlelist[page]
							pagerating = ratinglist[page]
							totalrating = totalrating + pagerating
							if "scp" in taglist[page]:
								scptotal +=1
							if  "tale" in taglist[page]:
								taletotal+=1
							if  "goi-format" in taglist[page]:
								goitotal+=1
				except AttributeError:
					pass
			else:
				if "author" in taglist[page]:
					if ":rewrite:" in authorlist[page]:
						bothauths = authorlist[page].split(":rewrite:")
						orgauth = bothauths[0]
						newauth = bothauths[1]
						if newauth == author:
							authorpage = "http://scp-wiki.net/"+page+" - "
					if author == authorlist[page]:
						authorpage = "http://scp-wiki.net/"+page+" - "
	except KeyError:
		pass
	plusauth = []
	moreauthors = 1
	plusauth.append(author)
	for authors in multimatch: #checks to see if multiple authors found
		z =0
		if ":rewrite:" in authors:
			continue
		for foundauthor in plusauth:
			if foundauthor ==authors:
				z =1
		if authors != author:
			if z == 0:
				moreauthors +=1
				plusauth.append(authors)
	if moreauthors>1:
		x = 0
		final = "Did you mean "
		for auth in plusauth:
			x+=1
			if x ==1:
				final+=auth+""
			if x ==2 and moreauthors ==2:
				final+=" or "+auth+"?"
			if x==2 and moreauthors >2:
				final+=", "+auth+""
			if x==3 and moreauthors ==3:
				final += ", or "+auth+"?"
			if x==3 and moreauthors >3:
				final += ", or "+auth+"? With " + str(moreauthors) + " more authors matching your query."
		return final
	avgrating = 0
	if taletotal+scptotal+goitotal is not 0: #just so no division by zero
		avgrating = totalrating/(taletotal+scptotal+goitotal)
	if not authpages: #if no author pages are added
		return "Author not found."
	final = authorpage+""+author +" has written " + str(scptotal) + " SCPs, "+str(taletotal)+" tales, and. "+str(goitotal)+" GOI formats. They have " + str(totalrating)+ " net upvotes with an average rating of " + str(avgrating) + ". Their most recent article is " + pagetitle + "(Rating:" + str(pagerating) + ")"
	return final
@hook.command("ad")
@hook.command("authordetails")
def authordetails(inp,nick=None):
	authpages = []
	totalrating = 0
	taletotal = 0
	scptotal = 0
	goitotal = 0
	pagerating = 0
	author = inp
	multimatch = []
	authorpage = ""
	found = 0
	exact = 0
	rewrite =0
	orgauth = ""
	newauth = ""
	rewriteauthor =0
	pagetitle = ""
	scprating = 0
	try:
		for page in scppages:
			if "scp" in taglist[page] or "tale" in taglist[page] or "goi-format" in taglist[page]: #makes sure only articles are counted
				if ":rewrite:" in authorlist[page]:
					bothauths = authorlist[page].split(":rewrite:")
					orgauth = bothauths[0]
					newauth = bothauths[1]
					if author == newauth:
						rewriteauthor = 1
				if author == authorlist[page] or rewriteauthor ==1:
					found =1
					rewriteauthor = 0

					authpages.append(page)
					pagetitle = titlelist[page]
					pagerating = ratinglist[page]
					totalrating = totalrating + pagerating
					if "scp" in taglist[page]:
						scptotal +=1
						scprating+=pagerating
					if  "tale" in taglist[page]:
						taletotal+=1
					if  "goi-format" in taglist[page]:
						goitotal+=1
				try:
					if inp.lower() in authorlist[page].lower(): #this just matches the author with the first author match
						if inp.lower() == authorlist[page].lower():
							exact +=1
						if exact == 1:
							author = authorlist[page]
							authpages = []
							multimatch = [authorlist[page]]
						elif exact < 1:
							multimatch.append(authorlist[page])
						if inp.lower() in authorlist[page].lower() and found == 0:
							author = authorlist[page]
							if ":rewrite:" in authorlist[page]:
								bothauths = authorlist[page].split(":rewrite:")
								orgauth = bothauths[0]
								newauth = bothauths[1]
								if inp.lower() in orgauth.lower():
									author = orgauth
								if inp.lower() in newauth.lower():
									author = newauth
								rewrite = 1
							found = 1
							authpages.append(page)
							pagetitle = titlelist[page]
							pagerating = ratinglist[page]
							totalrating = totalrating + pagerating
							if "scp" in taglist[page]:
								scptotal +=1
								scprating+=pagerating
							if  "tale" in taglist[page]:
								taletotal+=1
							if  "goi-format" in taglist[page]:
								goitotal+=1
				except AttributeError:
					pass
			else:
				if "author" in taglist[page]:
					if ":rewrite:" in authorlist[page]:
						bothauths = authorlist[page].split(":rewrite:")
						orgauth = bothauths[0]
						newauth = bothauths[1]
						if newauth == author:
							authorpage = "http://scp-wiki.net/"+page+" - "
					if author == authorlist[page]:
						authorpage = "http://scp-wiki.net/"+page+" - "
	except KeyError:
		pass
	plusauth = []
	moreauthors = 1
	plusauth.append(author)
	for authors in multimatch: #checks to see if multiple authors found
		z =0
		if ":rewrite:" in authors:
			continue
		for foundauthor in plusauth:
			if foundauthor ==authors:
				z =1
		if authors != author:
			if z == 0:
				moreauthors +=1
				plusauth.append(authors)
	if moreauthors>1:
		x = 0
		final = "Did you mean "
		for auth in plusauth:
			x+=1
			if x ==1:
				final+=auth+""
			if x ==2 and moreauthors ==2:
				final+=" or "+auth+"?"
			if x==2 and moreauthors >2:
				final+=", "+auth+""
			if x==3 and moreauthors ==3:
				final += ", or "+auth+"?"
			if x==3 and moreauthors >3:
				final += ", or "+auth+"? With " + str(moreauthors) + " more authors matching your query."
		return final
	avgrating = 0
	if taletotal+scptotal+goitotal is not 0: #just so no division by zero
		avgrating = totalrating/(taletotal+scptotal+goitotal)
	if not authpages: #if no author pages are added
		return "Author not found."
	api = wikidotapi.connection()
	api.Site = "alexandra-scp"
	table = ""
	for page in authpages:
		try:
			scptitle = scptitles[page]
			table+="||**"+titlelist[page]+"** (@@"+scptitle+"@@)||Rating: "+str(ratinglist[page])+"||Tags: "+str(taglist[page])+"|| http://scp-wiki.net/"+page+"||\n"
		except KeyError:
			table+="||**"+titlelist[page]+"**||Rating: "+str(ratinglist[page])+"||Tags: "+str(taglist[page])+"|| http://scp-wiki.net/"+page+"||\n"
	api.set_page_item(author.replace(" ","-").lower(), "content", "**Pages Authored:"+str(len(authpages))+"**\n**Number of SCPs Written:** "+str(scptotal)+"\n**Number of Tales Written:** "+str(taletotal)+"\n \n**Net Upvotes Received:** "+str(totalrating)+"\n**Average Rating per Page:** "+str(avgrating)+"\n**Average SCP Rating:** "+str(scprating/scptotal)+"\n++ Articles\n||Title||Rating||Tags||Link||\n"+table, create=True)
	return "Author detail page created http://alexandra-scp.wikidot.com/"+author.replace(" ","-").lower()
<|endoftext|>


<commit_before>#!/ms/dist/python/PROJ/core/2.5.0/bin/python
# ex: set expandtab softtabstop=4 shiftwidth=4: -*- cpy-indent-level: 4; indent-tabs-mode: nil -*-
# $Header$
# $Change$
# $DateTime$
# $Author$
# Copyright (C) 2008 Morgan Stanley
#
# This module is part of Aquilon
""" Left in its own module as it depends on many of the others to exist
    first to avoid circular dependencies """
import datetime

import sys
sys.path.append('../..')

from db import *
from service import Host
from configuration import CfgPath

from sqlalchemy import Column, Integer, Sequence, String, ForeignKey
from sqlalchemy.orm import mapper, relation, deferred

host     = Table('host', meta, autoload=True)
cfg_path = Table('cfg_path', meta, autoload=True)

build = Table('build', meta,
    Column('id', Integer, primary_key=True),
    Column('host_id', Integer,
           ForeignKey('host.id'), unique=True, nullable=False, index=True),
    Column('creation_date', DateTime, default=datetime.datetime.now),
    Column('comments', String(255), nullable=True))
build.create(checkfirst=True)

class Build(aqdbBase):
    """ Identifies the build process of a given Host.
        Parent of 'build_element' """
    @optional_comments
    def __init__(self,host):
        if isinstance(host,Host):
            self.host=host
        else:
            msg = 'Build object requires a Host object for its constructor'
            raise ArgumentError()
    def __repr__(self):
        return 'Build Information for %s'%(self.host.name)

mapper(Build,build,properties={
    'creation_date' : deferred(build.c.creation_date),
    'comments': deferred(build.c.comments)
})

build_element = Table('build_element', meta,
    Column('id',Integer, primary_key=True),
    Column('build_id', Integer,
           ForeignKey('build.id',
                      ondelete='CASCADE',
                      onupdate='CASCADE'),
           nullable=False,
           index=True),
    Column('cfg_path_id', Integer,
           ForeignKey('cfg_path.id',
                      ondelete='RESTRICT',
                      onupdate='CASCADE'),
           nullable=False),
    Column('order', Integer, nullable=False),
    Column('creation_date', DateTime, default=datetime.datetime.now),
    Column('comments', String(255), nullable=True))
build_element.create(checkfirst=True)

class BuildElement(aqdbBase):
    pass
mapper(BuildElement,build_element, properties={
    'build'         : relation(BuildElement),
    'cfg_path'      : relation(CfgPath),
    'creation_date' : deferred(build_element.c.creation_date),
    'comments'      : deferred(build_element.c.comments)
})
#unique on host,cfg_path
#unique on build_id/order
#primary key should be host_id/order
#somewhere in this or chost, a change should trigger an
#update_time column on some table to update

if __name__ == '__main__':
    #from aquilon.aqdb.utils.debug import ipshell
    #ipshell()
    assert(build_element)
<commit_msg>somehow a previously patched bug came back. damn you p4<commit_after>#!/ms/dist/python/PROJ/core/2.5.0/bin/python
# ex: set expandtab softtabstop=4 shiftwidth=4: -*- cpy-indent-level: 4; indent-tabs-mode: nil -*-
# $Header$
# $Change$
# $DateTime$
# $Author$
# Copyright (C) 2008 Morgan Stanley
#
# This module is part of Aquilon
""" Left in its own module as it depends on many of the others to exist
    first to avoid circular dependencies """
import datetime

import sys
sys.path.append('../..')

from db import *
from service import Host
from configuration import CfgPath

from sqlalchemy import Column, Integer, Sequence, String, ForeignKey
from sqlalchemy.orm import mapper, relation, deferred

host     = Table('host', meta, autoload=True)
cfg_path = Table('cfg_path', meta, autoload=True)

build = Table('build', meta,
    Column('id', Integer, primary_key=True),
    Column('host_id', Integer,
           ForeignKey('host.id'), unique=True, nullable=False, index=True),
    Column('creation_date', DateTime, default=datetime.datetime.now),
    Column('comments', String(255), nullable=True))
build.create(checkfirst=True)

class Build(aqdbBase):
    """ Identifies the build process of a given Host.
        Parent of 'build_element' """
    @optional_comments
    def __init__(self,host):
        if isinstance(host,Host):
            self.host=host
        else:
            msg = 'Build object requires a Host object for its constructor'
            raise ArgumentError()
    def __repr__(self):
        return 'Build Information for %s'%(self.host.name)

mapper(Build,build,properties={
    'creation_date' : deferred(build.c.creation_date),
    'comments': deferred(build.c.comments)
})

build_element = Table('build_element', meta,
    Column('id',Integer, primary_key=True),
    Column('build_id', Integer,
           ForeignKey('build.id',
                      ondelete='CASCADE',
                      onupdate='CASCADE'),
           nullable=False,
           index=True),
    Column('cfg_path_id', Integer,
           ForeignKey('cfg_path.id',
                      ondelete='RESTRICT',
                      onupdate='CASCADE'),
           nullable=False),
    Column('order', Integer, nullable=False),
    Column('creation_date', DateTime, default=datetime.datetime.now),
    Column('comments', String(255), nullable=True))
build_element.create(checkfirst=True)

class BuildElement(aqdbBase):
    pass
mapper(BuildElement,build_element, properties={
    'build'         : relation(Build),
    'cfg_path'      : relation(CfgPath),
    'creation_date' : deferred(build_element.c.creation_date),
    'comments'      : deferred(build_element.c.comments)
})
#unique on host,cfg_path
#unique on build_id/order
#primary key should be host_id/order
#somewhere in this or chost, a change should trigger an
#update_time column on some table to update

if __name__ == '__main__':
    #from aquilon.aqdb.utils.debug import ipshell
    #ipshell()
    assert(build_element)
<|endoftext|>


<commit_before>import json
import os
import re

import github3
from IGitt.GitHub.GitHub import GitHub, GitHubToken
from IGitt.GitLab.GitLab import GitLab, GitLabPrivateToken
from errbot import BotPlugin, re_botcmd

from plugins import constants

class LabHub(BotPlugin):
    """GitHub and GitLab utilities"""  # Ignore QuotesBear

    INVITE_SUCCESS = {
        'newcomers': 'Welcome @{}! :tada:\n\nTo get started, please follow '
                     'our [newcomers guide](https://coala.io/newcomer). Most '
                     'issues will be explained there and in linked pages - it '
                     'will save you a lot of time, just read it. *Really.*\n\n'
                     '*Do not take an issue if you don\'t understand it on '
                     'your own.*Especially if you are new you have to be aware '
                     'that getting started with an open source community is '
                     'not trivial: you will have to work hard and most likely '
                     'become a better coder than you are now just as we all '
                     'did.\n\nDon\'t get us wrong: we are *very* glad to have '
                     'you with us on this journey into open source! We will '
                     'also be there for you at all times to help you with '
                     'actual problems. :)',
        'developers': ' Wow @{}, you are a part of developers team now! :tada: '
                      'Welcome to ourcommunity!',
        'maintainers': ' @{} you seem to be awesome! You are now a maintainer! '
                       ':tada: Please go through '
                       'https://github.com/coala/coala/wiki/Membership'
    }

    GH_ORG_NAME = constants.GH_ORG_NAME
    GL_ORG_NAME = constants.GL_ORG_NAME

    def __init__(self, bot, name=None):
        super().__init__(bot, name)

        teams = dict()
        try:
            gh = github3.login(token=os.environ.get('GH_TOKEN'))
            assert gh is not None
        except AssertionError:
            self.log.error('Cannot create github object, please check GH_TOKEN')
        else:
            self.GH3_ORG = gh.organization(self.GH_ORG_NAME)
            for team in self.GH3_ORG.iter_teams():
                teams[team.name] = team

        self._teams = teams

        self.IGH = GitHub(GitHubToken(os.environ.get('GH_TOKEN')))
        self.IGL = GitLab(GitLabPrivateToken(os.environ.get('GL_TOKEN')))

        self.REPOS = dict()

        try:
            self.gh_repos = {repo.full_name.split('/')[1]: repo for repo in
                             filter(lambda x: (x.full_name.split('/')[0] ==
                                               self.GH_ORG_NAME),
                                    self.IGH.write_repositories)}
        except RuntimeError:
            self.log.exception('Something went wrong in fetching github repos.')
        else:
            self.REPOS.update(self.gh_repos)

        try:
            self.gl_repos = {repo.full_name.split('/')[1]: repo for repo in
                             filter(lambda x: (x.full_name.split('/')[0] ==
                                               self.GL_ORG_NAME),
                                    self.IGL.write_repositories)}
        except RuntimeError:  # pragma: no cover, for logging
            self.log.exception('Something went wrong in fetching gitlab repos.')
        else:
            self.REPOS.update(self.gl_repos)

    @property
    def TEAMS(self):
        return self._teams

    @TEAMS.setter
    def TEAMS(self, new):
        self._teams = new

    @re_botcmd(pattern=r'(?:(?:invite)|(?:inv))\s+(\w+)\s*(?:to)\s+(\w+)')
    def invite_cmd(self, msg, match):
        """
        Invite given user to given team. By default it invites to
        "newcomers" team.
        """
        invitee = match.group(1)
        inviter = msg.frm.nick

        team = (self.GH_ORG_NAME + ' newcomers' if match.group(2) is None
                else match.group(2))

        self.log.info('{} invited {} to {}'.format(inviter, invitee, team))

        if self.TEAMS[self.GH_ORG_NAME + ' maintainers'].is_member(inviter):
            valid_teams = ['newcomers', 'developers', 'maintainers']
            if team.lower() not in valid_teams:
                return 'Please select from one of the ' + ', '.join(valid_teams)
            team_mapping = {
                'newcomers': self.GH_ORG_NAME + ' newcomers',
                'developers': self.GH_ORG_NAME + ' developers',
                'maintainers': self.GH_ORG_NAME + ' maintainers'
            }

            # send the invite
            self.TEAMS[team_mapping[team.lower()]].invite(invitee)
            return self.INVITE_SUCCESS[team.lower()].format(invitee)
        else:
            return ('@{}, you are not a maintainer, only maintainers can invite'
                    ' other people. Nice try :poop:'.format(inviter))

    def callback_message(self, msg):
        """Invite the user whose message includes the holy 'hello world'"""
        if re.search(r'hello\s*,?\s*world', msg.body, flags=re.IGNORECASE):
            user = msg.frm.nick
            if not self.TEAMS[self.GH_ORG_NAME + ' newcomers'].is_member(user):
                # send the invite
                self.send(msg.frm,
                          self.INVITE_SUCCESS['newcomers'].format(user))
                self.TEAMS[self.GH_ORG_NAME + ' newcomers'].invite(user)

    @re_botcmd(pattern=r'(?:new|file) issue ([\w-]+?)(?: |\n)(.+?)(?:$|\n((?:.|\n)*))',  # Ignore LineLengthBear, PyCodeStyleBear
               flags=re.IGNORECASE)
    def create_issut_cmd(self, msg, match):
        """Create issues on GitHub and GitLab repositories."""  # Ignore QuotesBear, LineLengthBear, PyCodeStyleBear
        repo_name = match.group(1)
        iss_title = match.group(2)
        iss_description = match.group(3) if match.group(3) is not None else ''
        extra_msg = '\nOpened by {} '.format(msg.frm.nick)

        try:  # This is gitter backend specific
            extra_msg += ('at [gitter/{uri}](https://gitter.im/{uri}?at={idd})'
                          ''.format(
                             uri=msg.frm.room.uri,
                             idd=msg.extras['id']
                          ))
        except (AttributeError, KeyError):
            self.log.exception('An exception occured while getting the link to'
                               ' the message')

        if repo_name in self.REPOS:
            repo = self.REPOS[repo_name]
            iss = repo.create_issue(iss_title, iss_description + extra_msg)
            return 'Here you go: {}'.format(iss.url)
        else:
            return ('Can\'t create an issue for a repository that does not '
                    'exist. Please ensure that the repository is available '
                    'and owned by coala.')

    @re_botcmd(pattern=r'^unassign\s+https://(github|gitlab)\.com/([^/]+)/([^/]+)/issues/(\d+)',  # Ignore LineLengthBear, PyCodeStyleBear
               flags=re.IGNORECASE)
    def unassign_cmd(self, msg, match):
        """Unassign from an issue."""  # Ignore QuotesBear
        org = match.group(2)
        repo_name = match.group(3)
        issue_number = match.group(4)

        user = msg.frm.nick

        try:
            assert org == self.GH_ORG_NAME or org == self.GL_ORG_NAME
        except AssertionError:
            return 'Repository not owned by our org.'

        try:
            iss = self.REPOS[repo_name].get_issue(int(issue_number))
        except KeyError:
            return 'Repository doesn\'t exist.'
        else:
            if user in iss.assignees:
                iss.unassign(user)
                return '@{}, you are unassigned now :+1:'.format(user)
            else:
                return 'You are not an assignee on the issue.'

    @re_botcmd(pattern=r'mark\s+(wip|pending)\s+https://(github|gitlab)\.com/([^/]+)/([^/]+)/(pull|merge_requests)/(\d+)',  # Ignore LineLengthBear, PyCodeStyleBear
               flags=re.IGNORECASE)
    def mark_cmd(self, msg, match):
        """Mark a given PR/MR with status labels."""  # Ignore QuotesBear
        state, host, org, repo_name, xr, number = match.groups()

        if host.lower() == 'github':
            assert xr.lower() == 'pull'
        elif host.lower() == 'gitlab':
            assert xr.lower() == 'merge_requests'

        try:
            mr = self.REPOS[repo_name].get_mr(number)
        except KeyError:
            return 'Repository doesn\'t exist.'
        else:
            current_labels = list(mr.labels)
            if state == 'wip':
                pending_labels = ['process/pending_review',
                                  'process/pending review']
                for label in filter(lambda x: x in current_labels,
                                    pending_labels):
                    current_labels.remove(label)
                current_labels.append('process/wip')
                mr.labels = current_labels
                return ('The pull request {mr_link} is marked *work in progress'
                        '*. Use `cobot mark pending` or push to your branch if '
                        'feedback from the community is needed again.'.format(
                            mr_link=mr.url)
                        )
            else:
                wip_labels = ['process/wip']
                for label in filter(lambda x: x in current_labels,
                                    wip_labels):
                    current_labels.remove(label)
                current_labels.append('process/pending review')
                mr.labels = current_labels
                return ('The pull request {mr_link} is marked *pending review*,'
                        'so you will get feedback from the community. Use '
                        '`cobot mark wip` if there are known issues that should'
                        ' be corrected by the author.'.format(mr_link=mr.url))

    @re_botcmd(pattern=r'^assign\s+https://(github|gitlab)\.com/([^/]+)/([^/]+)/issues/(\d+)',  # Ignore LineLengthBear, PyCodeStyleBear
               flags=re.IGNORECASE)
    def assign_cmd(self, msg, match):
        """Assign to GitLab and GitHub issues."""  # Ignore QuotesBear
        org = match.group(2)
        repo_name = match.group(3)
        iss_number = match.group(4)

        user = msg.frm.nick

        try:
            assert org == self.GH_ORG_NAME or org == self.GL_ORG_NAME
        except AssertionError:
            yield 'Repository not owned by our org.'
            return

        checks = []

        def register_check(function):
            checks.append(function)
            return function

        @register_check
        def difficulty_level(user, iss):
            """
            True if:
            1. A newcomer is asking for assignment to low or newcomer issue.
            2. The user belongs to developers or maintainers team as well as
               newcomers team.
            False if
            1. A newcomer asks for assignment to an issue that has no difficulty
               label.
            2. A newcomer asks for assignment to an issue with difficulty higher
               than low.
            """
            if (self.TEAMS[self.GH_ORG_NAME + ' newcomers'].is_member(user) and
                not (self.TEAMS[self.GH_ORG_NAME + ' developers'].is_member(
                        user
                    ) or
                    self.TEAMS[self.GH_ORG_NAME + ' maintainers'].is_member(
                        user
                    ))):
                diff_labels = filter(lambda x: 'difficulty' in x, iss.labels)
                if list(filter(lambda x: ('low' in x) or ('newcomer' in x),
                               diff_labels)):
                    return True
                else:
                    return False
            elif self.GH3_ORG.is_member(user):
                return True

        def eligible(user, iss):
            for chk in checks:
                if not chk(user, iss):
                    return False
            return True

        eligility_conditions = [
            '- You must be a member of coala org to be assigned an issue '
            'If you are not a member yet, just type Hello World and '
            'corobo will invite you.',
            '- A newcomer cannot be assigned to an issue with a difficulty '
            'level higher than newcomer or low difficulty.',
        ]

        try:
            iss = self.REPOS[repo_name].get_issue(int(iss_number))
        except KeyError:
            yield 'Repository doesn\'t exist.'
        else:
            if not iss.assignees:
                if eligible(user, iss):
                    iss.assign(user)
                    yield ('Congratulations! You\'ve been assigned to the '
                           'issue. :tada:')
                else:
                    yield 'You are not eligible to be assigned to this issue.'
                    yield '\n'.join(eligility_conditions)
            else:
                yield ('The issue is already assigned to someone. Please '
                       'check if the assignee is still working on the issue, '
                       'if not, you should ask for reassignment.')
<commit_msg>labhub: Add usability with sub-groups<commit_after>import json
import os
import re

import github3
from IGitt.GitHub.GitHub import GitHub, GitHubToken
from IGitt.GitLab.GitLab import GitLab, GitLabPrivateToken
from errbot import BotPlugin, re_botcmd

from plugins import constants

class LabHub(BotPlugin):
    """GitHub and GitLab utilities"""  # Ignore QuotesBear

    INVITE_SUCCESS = {
        'newcomers': 'Welcome @{}! :tada:\n\nTo get started, please follow '
                     'our [newcomers guide](https://coala.io/newcomer). Most '
                     'issues will be explained there and in linked pages - it '
                     'will save you a lot of time, just read it. *Really.*\n\n'
                     '*Do not take an issue if you don\'t understand it on '
                     'your own.*Especially if you are new you have to be aware '
                     'that getting started with an open source community is '
                     'not trivial: you will have to work hard and most likely '
                     'become a better coder than you are now just as we all '
                     'did.\n\nDon\'t get us wrong: we are *very* glad to have '
                     'you with us on this journey into open source! We will '
                     'also be there for you at all times to help you with '
                     'actual problems. :)',
        'developers': ' Wow @{}, you are a part of developers team now! :tada: '
                      'Welcome to ourcommunity!',
        'maintainers': ' @{} you seem to be awesome! You are now a maintainer! '
                       ':tada: Please go through '
                       'https://github.com/coala/coala/wiki/Membership'
    }

    GH_ORG_NAME = constants.GH_ORG_NAME
    GL_ORG_NAME = constants.GL_ORG_NAME

    def __init__(self, bot, name=None):
        super().__init__(bot, name)

        teams = dict()
        try:
            gh = github3.login(token=os.environ.get('GH_TOKEN'))
            assert gh is not None
        except AssertionError:
            self.log.error('Cannot create github object, please check GH_TOKEN')
        else:
            self.GH3_ORG = gh.organization(self.GH_ORG_NAME)
            for team in self.GH3_ORG.iter_teams():
                teams[team.name] = team

        self._teams = teams

        self.IGH = GitHub(GitHubToken(os.environ.get('GH_TOKEN')))
        self.IGL = GitLab(GitLabPrivateToken(os.environ.get('GL_TOKEN')))

        self.REPOS = dict()

        try:
            self.gh_repos = {repo.full_name.split('/')[-1]: repo for repo in
                             filter(lambda x: (x.full_name.split('/')[0] ==
                                               self.GH_ORG_NAME),
                                    self.IGH.write_repositories)}
        except RuntimeError:
            self.log.exception('Something went wrong in fetching github repos.')
        else:
            self.REPOS.update(self.gh_repos)

        try:
            self.gl_repos = {repo.full_name.split('/')[-1]: repo for repo in
                             filter(lambda x: (x.full_name.split('/')[0] ==
                                               self.GL_ORG_NAME),
                                    self.IGL.write_repositories)}
        except RuntimeError:  # pragma: no cover, for logging
            self.log.exception('Something went wrong in fetching gitlab repos.')
        else:
            self.REPOS.update(self.gl_repos)

    @property
    def TEAMS(self):
        return self._teams

    @TEAMS.setter
    def TEAMS(self, new):
        self._teams = new

    @re_botcmd(pattern=r'(?:(?:invite)|(?:inv))\s+(\w+)\s*(?:to)\s+(\w+)')
    def invite_cmd(self, msg, match):
        """
        Invite given user to given team. By default it invites to
        "newcomers" team.
        """
        invitee = match.group(1)
        inviter = msg.frm.nick

        team = (self.GH_ORG_NAME + ' newcomers' if match.group(2) is None
                else match.group(2))

        self.log.info('{} invited {} to {}'.format(inviter, invitee, team))

        if self.TEAMS[self.GH_ORG_NAME + ' maintainers'].is_member(inviter):
            valid_teams = ['newcomers', 'developers', 'maintainers']
            if team.lower() not in valid_teams:
                return 'Please select from one of the ' + ', '.join(valid_teams)
            team_mapping = {
                'newcomers': self.GH_ORG_NAME + ' newcomers',
                'developers': self.GH_ORG_NAME + ' developers',
                'maintainers': self.GH_ORG_NAME + ' maintainers'
            }

            # send the invite
            self.TEAMS[team_mapping[team.lower()]].invite(invitee)
            return self.INVITE_SUCCESS[team.lower()].format(invitee)
        else:
            return ('@{}, you are not a maintainer, only maintainers can invite'
                    ' other people. Nice try :poop:'.format(inviter))

    def callback_message(self, msg):
        """Invite the user whose message includes the holy 'hello world'"""
        if re.search(r'hello\s*,?\s*world', msg.body, flags=re.IGNORECASE):
            user = msg.frm.nick
            if not self.TEAMS[self.GH_ORG_NAME + ' newcomers'].is_member(user):
                # send the invite
                self.send(msg.frm,
                          self.INVITE_SUCCESS['newcomers'].format(user))
                self.TEAMS[self.GH_ORG_NAME + ' newcomers'].invite(user)

    @re_botcmd(pattern=r'(?:new|file) issue ([\w-]+?)(?: |\n)(.+?)(?:$|\n((?:.|\n)*))',  # Ignore LineLengthBear, PyCodeStyleBear
               flags=re.IGNORECASE)
    def create_issut_cmd(self, msg, match):
        """Create issues on GitHub and GitLab repositories."""  # Ignore QuotesBear, LineLengthBear, PyCodeStyleBear
        repo_name = match.group(1)
        iss_title = match.group(2)
        iss_description = match.group(3) if match.group(3) is not None else ''
        extra_msg = '\nOpened by {} '.format(msg.frm.nick)

        try:  # This is gitter backend specific
            extra_msg += ('at [gitter/{uri}](https://gitter.im/{uri}?at={idd})'
                          ''.format(
                             uri=msg.frm.room.uri,
                             idd=msg.extras['id']
                          ))
        except (AttributeError, KeyError):
            self.log.exception('An exception occured while getting the link to'
                               ' the message')

        if repo_name in self.REPOS:
            repo = self.REPOS[repo_name]
            iss = repo.create_issue(iss_title, iss_description + extra_msg)
            return 'Here you go: {}'.format(iss.url)
        else:
            return ('Can\'t create an issue for a repository that does not '
                    'exist. Please ensure that the repository is available '
                    'and owned by the org.')

    @re_botcmd(pattern=r'^unassign\s+https://(github|gitlab)\.com/([^/]+)/([^/]+)/issues/(\d+)',  # Ignore LineLengthBear, PyCodeStyleBear
               flags=re.IGNORECASE)
    def unassign_cmd(self, msg, match):
        """Unassign from an issue."""  # Ignore QuotesBear
        org = match.group(2)
        repo_name = match.group(3)
        issue_number = match.group(4)

        user = msg.frm.nick

        try:
            assert org == self.GH_ORG_NAME or org == self.GL_ORG_NAME
        except AssertionError:
            return 'Repository not owned by our org.'

        try:
            iss = self.REPOS[repo_name].get_issue(int(issue_number))
        except KeyError:
            return 'Repository doesn\'t exist.'
        else:
            if user in iss.assignees:
                iss.unassign(user)
                return '@{}, you are unassigned now :+1:'.format(user)
            else:
                return 'You are not an assignee on the issue.'

    @re_botcmd(pattern=r'mark\s+(wip|pending)\s+https://(github|gitlab)\.com/([^/]+)/([^/]+)/(pull|merge_requests)/(\d+)',  # Ignore LineLengthBear, PyCodeStyleBear
               flags=re.IGNORECASE)
    def mark_cmd(self, msg, match):
        """Mark a given PR/MR with status labels."""  # Ignore QuotesBear
        state, host, org, repo_name, xr, number = match.groups()

        if host.lower() == 'github':
            assert xr.lower() == 'pull'
        elif host.lower() == 'gitlab':
            assert xr.lower() == 'merge_requests'

        try:
            mr = self.REPOS[repo_name].get_mr(number)
        except KeyError:
            return 'Repository doesn\'t exist.'
        else:
            current_labels = list(mr.labels)
            if state == 'wip':
                pending_labels = ['process/pending_review',
                                  'process/pending review']
                for label in filter(lambda x: x in current_labels,
                                    pending_labels):
                    current_labels.remove(label)
                current_labels.append('process/wip')
                mr.labels = current_labels
                return ('The pull request {mr_link} is marked *work in progress'
                        '*. Use `cobot mark pending` or push to your branch if '
                        'feedback from the community is needed again.'.format(
                            mr_link=mr.url)
                        )
            else:
                wip_labels = ['process/wip']
                for label in filter(lambda x: x in current_labels,
                                    wip_labels):
                    current_labels.remove(label)
                current_labels.append('process/pending review')
                mr.labels = current_labels
                return ('The pull request {mr_link} is marked *pending review*,'
                        'so you will get feedback from the community. Use '
                        '`cobot mark wip` if there are known issues that should'
                        ' be corrected by the author.'.format(mr_link=mr.url))

    @re_botcmd(pattern=r'^assign\s+https://(github|gitlab)\.com/([^/]+)/([^/]+/)+issues/(\d+)',  # Ignore LineLengthBear, PyCodeStyleBear
               flags=re.IGNORECASE)
    def assign_cmd(self, msg, match):
        """Assign to GitLab and GitHub issues."""  # Ignore QuotesBear
        org = match.group(2)
        repo_name = match.group(3)[:-1]
        iss_number = match.group(4)

        user = msg.frm.nick

        try:
            assert org == self.GH_ORG_NAME or org == self.GL_ORG_NAME
        except AssertionError:
            yield 'Repository not owned by our org.'
            return

        checks = []

        def register_check(function):
            checks.append(function)
            return function

        @register_check
        def difficulty_level(user, iss):
            """
            True if:
            1. A newcomer is asking for assignment to low or newcomer issue.
            2. The user belongs to developers or maintainers team as well as
               newcomers team.
            False if
            1. A newcomer asks for assignment to an issue that has no difficulty
               label.
            2. A newcomer asks for assignment to an issue with difficulty higher
               than low.
            """
            if (self.TEAMS[self.GH_ORG_NAME + ' newcomers'].is_member(user) and
                not (self.TEAMS[self.GH_ORG_NAME + ' developers'].is_member(
                        user
                    ) or
                    self.TEAMS[self.GH_ORG_NAME + ' maintainers'].is_member(
                        user
                    ))):
                diff_labels = filter(lambda x: 'difficulty' in x, iss.labels)
                if list(filter(lambda x: ('low' in x) or ('newcomer' in x),
                               diff_labels)):
                    return True
                else:
                    return False
            elif self.GH3_ORG.is_member(user):
                return True

        def eligible(user, iss):
            for chk in checks:
                if not chk(user, iss):
                    return False
            return True

        eligility_conditions = [
            '- You must be a member of {} org to be assigned an issue '
            'If you are not a member yet, just type Hello World and '
            'corobo will invite you.'.format(self.GH_ORG_NAME),
            '- A newcomer cannot be assigned to an issue with a difficulty '
            'level higher than newcomer or low difficulty.',
        ]

        try:
            iss = self.REPOS[repo_name].get_issue(int(iss_number))
        except KeyError:
            yield 'Repository doesn\'t exist.'
        else:
            if not iss.assignees:
                if eligible(user, iss):
                    iss.assign(user)
                    yield ('Congratulations! You\'ve been assigned to the '
                           'issue. :tada:')
                else:
                    yield 'You are not eligible to be assigned to this issue.'
                    yield '\n'.join(eligility_conditions)
            else:
                yield ('The issue is already assigned to someone. Please '
                       'check if the assignee is still working on the issue, '
                       'if not, you should ask for reassignment.')
<|endoftext|>


<commit_before>from plugin import Plugin
from config import cmd_lfm
from config import LastFMAPIKey as lfm_key
from utils import create_logger
import requests

class LastFM(Plugin):
    is_global = True
    log = create_logger(cmd_lfm)

    async def on_message(self, message, pfx):
        if message.content.startswith(pfx + cmd_lfm + ' '):
            await self.client.send_typing(message.channel)
            cmd_name = 'LastFM'
            self.log.info('User %s [%s] on server %s [%s], used the ' + cmd_name + ' command on #%s channel',
                          message.author,
                          message.author.id, message.server.name, message.server.id, message.channel)
            lfm_input = message.content[len(pfx) + len(cmd_lfm) + 1:]
            lfm_user, ignore, no_of_songs = lfm_input.partition(' ')
            lfm_url = 'http://ws.audioscrobbler.com/2.0/?method=user.gettoptracks&user=' + lfm_user + '&api_key=' + lfm_key + '&format=json'
            lfm_data = requests.get(lfm_url).json()
            no_of_songs_in_list = len(lfm_data['toptracks']['track'])
            try:
                tr1_n = lfm_data['toptracks']['track'][0]['name']
                tr1_a = lfm_data['toptracks']['track'][0]['artist']['name']
                tr2_n = lfm_data['toptracks']['track'][1]['name']
                tr2_a = lfm_data['toptracks']['track'][1]['artist']['name']
                tr3_n = lfm_data['toptracks']['track'][2]['name']
                tr3_a = lfm_data['toptracks']['track'][2]['artist']['name']
                tr4_n = lfm_data['toptracks']['track'][3]['name']
                tr4_a = lfm_data['toptracks']['track'][3]['artist']['name']
                tr5_n = lfm_data['toptracks']['track'][4]['name']
                tr5_a = lfm_data['toptracks']['track'][4]['artist']['name']
                top_tracks_text = ('Top 5 Tracks for the user `' + lfm_user + '`:\n```' +
                                   '\n #1: ' + tr1_n + ' by ' + tr1_a +
                                   '\n #2: ' + tr2_n + ' by ' + tr2_a +
                                   '\n #3: ' + tr3_n + ' by ' + tr3_a +
                                   '\n #4: ' + tr4_n + ' by ' + tr4_a +
                                   '\n #5: ' + tr5_n + ' by ' + tr5_a +
                                   '\n```')
                await self.client.send_message(message.channel, top_tracks_text)
            except:
                try:
                    await self.client.send_message(message.channel, lfm_data['message'])
                except:
                    #try:
                        #await self.client.send_message(message.channel,'There are only: `' + str(no_of_songs_in_list) + '` in your list.')
                    #except:
                        await self.client.send_message(message.channel, 'We seem to have ran into an error.')
<commit_msg>LastFM code improvement<commit_after>from plugin import Plugin
from config import cmd_lfm
from config import LastFMAPIKey as lfm_key
from utils import create_logger
import requests

class LastFM(Plugin):
    is_global = True
    log = create_logger(cmd_lfm)

    async def on_message(self, message, pfx):
        if message.content.startswith(pfx + cmd_lfm + ' '):
            await self.client.send_typing(message.channel)
            cmd_name = 'LastFM'
            self.log.info('User %s [%s] on server %s [%s], used the ' + cmd_name + ' command on #%s channel',
                          message.author,
                          message.author.id, message.server.name, message.server.id, message.channel)
            lfm_input = message.content[len(pfx) + len(cmd_lfm) + 1:]
            lfm_user, ignore, no_of_songs = lfm_input.partition(' ')
            lfm_url = 'http://ws.audioscrobbler.com/2.0/?method=user.gettoptracks&user=' + lfm_user + '&api_key=' + lfm_key + '&format=json'
            lfm_data = requests.get(lfm_url).json()
            no_of_songs_in_list = len(lfm_data['toptracks']['track'])
            try:
                top_tracks_text = ('Top 5 Tracks for the user `' + lfm_user + '`:\n```')
                for i in range(0, 5):
                    name = lfm_data['toptracks']['track'][i]['name']
                    artist = lfm_data['toptracks']['track'][i]['artist']['name']
                    top_tracks_text += '\n #' + i + ': ' + name + ' by ' + artist
                top_tracks_text += '\n```'
                await self.client.send_message(message.channel, top_tracks_text)
            except:
                try:
                    await self.client.send_message(message.channel, lfm_data['message'])
                except:
                    #try:
                        #await self.client.send_message(message.channel,'There are only: `' + str(no_of_songs_in_list) + '` in your list.')
                    #except:
                        await self.client.send_message(message.channel, 'We seem to have ran into an error.')
<|endoftext|>


<commit_before>import asyncio
from plugin_manager import BasePlugin
from plugins.channel_manager import ChannelNotFoundError
from utils import split_message

class DiscordLogger(BasePlugin):
    name = "logger"
    default_config = {
        "default": {
            "log_events": [
                "message_delete",
                "message_edit",
                "member_join",
                "member_remove"
            ]
        }
    }

    async def activate(self):
        self.log_items = {}
        self.active = True
        asyncio.ensure_future(self._dump_logs())

    async def deactivate(self):
        self.active = False

    async def _dump_logs(self):
        while self.active:
            await asyncio.sleep(1)
            for guild in self.client.guilds:
                gid = str(guild.id)
                try:
                    logchan = self.plugins.channel_manager.get_channel(guild, "logs")
                except ChannelNotFoundError:
                    continue
                if gid in self.log_items and self.log_items[gid]:
                    logs = "\n".join(self.log_items[gid])
                    for msg in split_message(logs, splitter="\n"):
                        await logchan.send(msg)
                    self.log_items[gid] = []

    async def on_message_delete(self, msg):
        gid = str(msg.guild.id)
        if gid not in self.plugin_config:
            self.plugin_config[gid] = self.plugin_config["default"]
        if "message_delete" in self.plugin_config[gid].log_events and msg.author != self.client.user:
            uname = str(msg.author)
            contents = msg.clean_content
            attaches = ""
            links = ""
            if msg.attachments:
                self.logger.debug(msg.attachments)
                links = ", ".join([x.url for x in msg.attachments])
                attaches = f"\n**Attachments:** `{links}`"
            self.logger.debug(f"User {uname}'s message in {msg.channel.name} was deleted.\nContents: {contents}\n"
                              f"Attachments: {links}")
            if gid not in self.log_items:
                self.log_items[gid] = []
            self.log_items[gid].append(f"**WARNING: User {uname}'s message in {msg.channel.mention} was deleted. "
                                       f"ANALYSIS: Contents:**\n{contents}{attaches}")

    async def on_message_edit(self, before, after):
        gid = str(after.guild.id)
        if gid not in self.plugin_config:
            self.plugin_config[gid] = self.plugin_config["default"]
        if "message_edit" in self.plugin_config[gid].log_events and after.author != self.client.user:
            uname = str(after.author)
            old_contents = before.clean_content
            contents = after.clean_content
            if old_contents == contents:
                return
            self.logger.debug(f"User {uname} edited their message in {after.channel.name}. \n"
                              f"Old contents: {old_contents}\nNew contents: {contents}")
            if gid not in self.log_items:
                self.log_items[gid] = []
            self.log_items[gid].append(f"**WARNING: User {uname} edited their message in {after.channel.mention}. "
                                       f"ANALYSIS:**\n**Old contents:** {old_contents}\n**New contents:** {contents}")

    async def on_member_join(self, member):
        gid = str(member.guild.id)
        if gid not in self.plugin_config:
            self.plugin_config[gid] = self.plugin_config["default"]
        if "member_join" in self.plugin_config[gid].log_events:
            uname = str(member)
            self.logger.debug(f"User {uname} joined {member.guild.name}.")
            if gid not in self.log_items:
                self.log_items[gid] = []
            self.log_items[gid].append(f"**NEW USER DETECTED: {uname}.**")

    async def on_member_remove(self, member):
        gid = str(member.guild.id)
        if gid not in self.plugin_config:
            self.plugin_config[gid] = self.plugin_config["default"]
        if "member_remove" in self.plugin_config[gid].log_events:
            uname = str(member)
            self.logger.debug(f"User {uname} left {member.guild.name}.")
            if gid not in self.log_items:
                self.log_items[gid] = []
            self.log_items[gid].append(f"**WARNING: User {uname} has left the server.**")
<commit_msg>Add creation time of message to edit/delete logging<commit_after>import asyncio
from plugin_manager import BasePlugin
from plugins.channel_manager import ChannelNotFoundError
from utils import split_message

class DiscordLogger(BasePlugin):
    name = "logger"
    default_config = {
        "default": {
            "log_events": [
                "message_delete",
                "message_edit",
                "member_join",
                "member_remove"
            ]
        }
    }

    async def activate(self):
        self.log_items = {}
        self.active = True
        asyncio.ensure_future(self._dump_logs())

    async def deactivate(self):
        self.active = False

    async def _dump_logs(self):
        while self.active:
            await asyncio.sleep(1)
            for guild in self.client.guilds:
                gid = str(guild.id)
                try:
                    logchan = self.plugins.channel_manager.get_channel(guild, "logs")
                except ChannelNotFoundError:
                    continue
                if gid in self.log_items and self.log_items[gid]:
                    logs = "\n".join(self.log_items[gid])
                    for msg in split_message(logs, splitter="\n"):
                        await logchan.send(msg)
                    self.log_items[gid] = []

    async def on_message_delete(self, msg):
        gid = str(msg.guild.id)
        if gid not in self.plugin_config:
            self.plugin_config[gid] = self.plugin_config["default"]
        if "message_delete" in self.plugin_config[gid].log_events and msg.author != self.client.user:
            uname = str(msg.author)
            contents = msg.clean_content
            msgtime = msg.created_at.strftime("%Y-%m-%d @ %H:%M:%S")
            attaches = ""
            links = ""
            if msg.attachments:
                links = ", ".join([x.url for x in msg.attachments])
                attaches = f"\n**Attachments:** `{links}`"
            self.logger.debug(f"User {uname}'s message at {msgtime} in {msg.channel.name} was deleted.\n"
                              f"Contents: {contents}\nAttachments: {links}")
            if gid not in self.log_items:
                self.log_items[gid] = []
            self.log_items[gid].append(f"**WARNING: User {uname}'s message at `{msgtime}` in {msg.channel.mention} was"
                                       f" deleted. ANALYSIS: Contents:**\n{contents}{attaches}")

    async def on_message_edit(self, before, after):
        gid = str(after.guild.id)
        if gid not in self.plugin_config:
            self.plugin_config[gid] = self.plugin_config["default"]
        if "message_edit" in self.plugin_config[gid].log_events and after.author != self.client.user:
            uname = str(after.author)
            old_contents = before.clean_content
            contents = after.clean_content
            msgtime = after.created_at.strftime("%Y-%m-%d @ %H:%M:%S")
            if old_contents == contents:
                return
            self.logger.debug(f"User {uname} edited their message at {msgtime} in {after.channel.name}. \n"
                              f"Old contents: {old_contents}\nNew contents: {contents}")
            if gid not in self.log_items:
                self.log_items[gid] = []
            self.log_items[gid].append(f"**WARNING: User {uname} edited their message at `{msgtime}` in "
                                       f"{after.channel.mention}. ANALYSIS:**\n"
                                       f"**Old contents:** {old_contents}\n**New contents:** {contents}")

    async def on_member_join(self, member):
        gid = str(member.guild.id)
        if gid not in self.plugin_config:
            self.plugin_config[gid] = self.plugin_config["default"]
        if "member_join" in self.plugin_config[gid].log_events:
            uname = str(member)
            self.logger.debug(f"User {uname} joined {member.guild.name}.")
            if gid not in self.log_items:
                self.log_items[gid] = []
            self.log_items[gid].append(f"**NEW USER DETECTED: {uname}.**")

    async def on_member_remove(self, member):
        gid = str(member.guild.id)
        if gid not in self.plugin_config:
            self.plugin_config[gid] = self.plugin_config["default"]
        if "member_remove" in self.plugin_config[gid].log_events:
            uname = str(member)
            self.logger.debug(f"User {uname} left {member.guild.name}.")
            if gid not in self.log_items:
                self.log_items[gid] = []
            self.log_items[gid].append(f"**WARNING: User {uname} has left the server.**")
<|endoftext|>


<commit_before>import io
import unittest

class Plugin:
    def on_command(self, bot, msg, stdin, stdout, reply):
        if len(msg["args"]) >= 2:
            user = msg["args"][1]
            message = " ".join(msg["args"][2:])
            if not message:
                message = stdin.read().strip()

            print("{0}: {1}".format(user, message), file=stdout)

    def on_help(self):
        return "Usage: sendto <user> [<message>]"

class Test(unittest.TestCase):
    def setUp(self):
        self.plugin = Plugin()

    def test_words(self):
        stdout = io.StringIO()
        self.plugin.on_command(None, {"args": [None, "thing", "hello"]}, None, stdout, None)
        self.assertEqual(stdout.getvalue().strip(), "thing: hello")

    def test_help(self):
        self.assertTrue(self.plugin.on_help())
<commit_msg>Show help message is no args passed to sendto<commit_after>import io
import unittest

class Plugin:
    def on_command(self, bot, msg, stdin, stdout, reply):
        if len(msg["args"]) >= 2:
            user = msg["args"][1]
            message = " ".join(msg["args"][2:])
            if not message:
                message = stdin.read().strip()

            print("{0}: {1}".format(user, message), file=stdout)
        else:
            print(self.on_help(), file=stdout)

    def on_help(self):
        return "Usage: sendto <user> [<message>]"

class Test(unittest.TestCase):
    def setUp(self):
        self.plugin = Plugin()

    def test_words(self):
        stdout = io.StringIO()
        self.plugin.on_command(None, {"args": [None, "thing", "hello"]}, None, stdout, None)
        self.assertEqual(stdout.getvalue().strip(), "thing: hello")

    def test_help(self):
        self.assertTrue(self.plugin.on_help())
<|endoftext|>


<commit_before>from sleekxmpp.xmlstream import ElementBase, ET, JID, register_stanza_plugin
from sleekxmpp import Iq
from sleekxmpp.plugins.base import base_plugin
from sleekxmpp.xmlstream.handler.callback import Callback
from sleekxmpp.xmlstream.matcher.xpath import MatchXPath

class upload(base_plugin):

    def plugin_init(self):
        self.description = "upload files via http"
        self.xep = "0999"
        self.xmpp['xep_0030'].add_feature("urn:xmpp:http:upload")
        self.xmpp.register_handler(
            Callback('Upload request',
                MatchXPath('{%s}iq/{urn:xmpp:http:upload}request' % self.xmpp.default_ns),
                self._handleUpload))
        register_stanza_plugin(Iq, UploadRequest)
        register_stanza_plugin(Iq, UploadSlot)

    def _handleUpload(self, iq):
        if iq['type'] == 'get':
            self.xmpp.event('request_upload_slot',iq)

class UploadRequest(ElementBase):
    namespace = "urn:xmpp:http:upload"
    name = "request"
    plugin_attrib = "request"
    interfaces = set(('size','filename'))
    sub_interfaces = interfaces

class UploadSlot(ElementBase):
    namespace = "urn:xmpp:http:upload"
    name = "slot"
    plugin_attrib = "slot"
    interfaces = set(('put','get'))
    sub_interfaces = interfaces
<commit_msg>changed namespace<commit_after>from sleekxmpp.xmlstream import ElementBase, ET, JID, register_stanza_plugin
from sleekxmpp import Iq
from sleekxmpp.plugins.base import base_plugin
from sleekxmpp.xmlstream.handler.callback import Callback
from sleekxmpp.xmlstream.matcher.xpath import MatchXPath

class upload(base_plugin):

    def plugin_init(self):
        self.description = "upload files via http"
        self.xep = "0999"
        self.xmpp['xep_0030'].add_feature("eu:siacs:conversations:http:upload")
        self.xmpp.register_handler(
            Callback('Upload request',
                MatchXPath('{%s}iq/{eu:siacs:conversations:http:upload}request' % self.xmpp.default_ns),
                self._handleUpload))
        register_stanza_plugin(Iq, UploadRequest)
        register_stanza_plugin(Iq, UploadSlot)

    def _handleUpload(self, iq):
        if iq['type'] == 'get':
            self.xmpp.event('request_upload_slot',iq)

class UploadRequest(ElementBase):
    namespace = "eu:siacs:conversations:http:upload"
    name = "request"
    plugin_attrib = "request"
    interfaces = set(('size','filename'))
    sub_interfaces = interfaces

class UploadSlot(ElementBase):
    namespace = "eu:siacs:conversations:http:upload"
    name = "slot"
    plugin_attrib = "slot"
    interfaces = set(('put','get'))
    sub_interfaces = interfaces
<|endoftext|>


<commit_before>from templates import Plugin
from zipfile import ZipFile
from elftools.elf.elffile import ELFFile
from cStringIO import StringIO
import gzip, string, re

def get_strings(data):
	result = ""
	for c in data:
		if c in string.printable:
			result += c
			continue
		if len(result) >= 8:
			yield result
		result = ""

class Z3Code(Plugin):

	NAME = 'Z3Core'

	WHITELISTED_DLL = ['System_Core_dll', 'NLua_Android_dll',
				'KopiLua_Android_dll', 'Mono_Android_dll',
				'Z_VFS_Android_dll', 'Xamarin_Mobile_dll',
				'mscorlib_dll', 'System_dll',
				]

	def recon(self):
		z = ZipFile(self.filename)
		if not 'lib/armeabi-v7a/libmonodroid.so' in z.namelist() or not 'lib/armeabi-v7a/libmonodroid_bundle_app.so' in z.namelist():
			return False
		f = z.open('lib/armeabi-v7a/libmonodroid_bundle_app.so')
		f = StringIO(f.read())
		elffile = ELFFile(f)
		section = elffile.get_section_by_name('.dynsym')
		for symbol in section.iter_symbols():
			if symbol['st_shndx']  != 'SHN_UNDEF' and symbol.name == 'mono_mkbundle_init':
				return True
        	return False

	def extract(self):
		c2 = []
		z = ZipFile(self.filename)
		data = z.open('lib/armeabi-v7a/libmonodroid_bundle_app.so').read()
		f = StringIO(data)
		elffile = ELFFile(f)
		section = elffile.get_section_by_name('.dynsym')
		for symbol in section.iter_symbols():
			if symbol['st_shndx'] != 'SHN_UNDEF' and symbol.name.startswith('assembly_data_'):
				if symbol.name[14:] in self.WHITELISTED_DLL:
					continue
				dll_data = data[symbol['st_value']:symbol['st_value']+symbol['st_size']]
				dll_data = gzip.GzipFile(fileobj=StringIO(dll_data)).read()
				regexp = 'h\x00t\x00t\x00p\x00:\x00/\x00/\x00.*?\x00\x00'
				s = map(lambda x : x.replace('\x00',''), re.compile(regexp, flags=re.UNICODE).findall(dll_data))
				c2.extend(s)
		return {'c2': c2}

<commit_msg>Sanitization of z3core c&c + better lookup for the right .NET DLL<commit_after>from templates import Plugin
from zipfile import ZipFile
from elftools.elf.elffile import ELFFile
from cStringIO import StringIO
import gzip, string, re

def get_strings(data):
	result = ""
	for c in data:
		if c in string.printable:
			result += c
			continue
		if len(result) >= 8:
			yield result
		result = ""

class Z3Code(Plugin):

	NAME = 'Z3Core'

	WHITELISTED_DLL = ['System_Core_dll', 'NLua_Android_dll',
				'KopiLua_Android_dll', 'Mono_Android_dll',
				'Z_VFS_Android_dll', 'Xamarin_Mobile_dll',
				'mscorlib_dll', 'System_dll', 'Mono_Android_Export_dll',
				'System_Xml_dll'
				]

	def recon(self):
		z = ZipFile(self.filename)
		bundle = False
		if 'lib/armeabi-v7a/libmonodroid.so' in z.namelist() and 'lib/armeabi-v7a/libmonodroid_bundle_app.so' in z.namelist():
			bundle = 'lib/armeabi-v7a/libmonodroid_bundle_app.so'
		elif 'lib/armeabi/libmonodroid.so' in z.namelist() and 'lib/armeabi/libmonodroid_bundle_app.so' in z.namelist():
			bundle = 'lib/armeabi/libmonodroid_bundle_app.so'
		if not bundle:
			return False
		self.bundle = bundle
		f = z.open(bundle)
		f = StringIO(f.read())
		elffile = ELFFile(f)
		section = elffile.get_section_by_name('.dynsym')
		for symbol in section.iter_symbols():
			if symbol['st_shndx']  != 'SHN_UNDEF' and symbol.name == 'mono_mkbundle_init':
				return True
        	return False

	def extract(self):
		c2 = []
		z = ZipFile(self.filename)
		data = z.open(self.bundle).read()
		f = StringIO(data)
		elffile = ELFFile(f)
		section = elffile.get_section_by_name('.dynsym')
		for symbol in section.iter_symbols():
			if symbol['st_shndx'] != 'SHN_UNDEF' and symbol.name.startswith('assembly_data_'):
				if symbol.name[14:] in self.WHITELISTED_DLL:
					continue
				dll_data = data[symbol['st_value']:symbol['st_value']+symbol['st_size']]
				dll_data = gzip.GzipFile(fileobj=StringIO(dll_data)).read()
				regexp = 'h\x00t\x00t\x00p\x00:\x00/\x00/\x00.*?\x00\x00'
				s = map(lambda x : x.replace('\x00',''), re.compile(regexp, flags=re.UNICODE).findall(dll_data))
				for entry in s:
					cc = ""
					for c in entry:
						if c in string.printable:
							cc += c
						else:
							break
					c2.append(cc)
		return {'c2': c2}

<|endoftext|>


<commit_before>from .config import parse
from .target import Target
from .__version__ import __version__

from argparse import ArgumentParser

import logging
import os
import sys

def main():
    setup_logging()

    parser = ArgumentParser(prog='construi', description='Run construi')

    parser.add_argument('target', metavar='TARGET', nargs='?')
    parser.add_argument('--basedir', metavar='DIR', default=os.getcwd())
    parser.add_argument('--version', action='version', version=__version__)
    parser.add_argument('-T', '--list-targets', action='store_true')

    args = parser.parse_args()

    config = load_config(args)

    if args.list_targets:
        list_targets(config)

    target = args.target or config.default

    Target(config.for_target(target)).run()

def load_config(args):
    return parse(args.basedir, 'construi.yml')

def setup_logging():
    root_logger = logging.getLogger()
    root_logger.addHandler(logging.StreamHandler(sys.stdout))
    root_logger.setLevel(logging.INFO)

    logging.getLogger("requests").propagate = False

def list_targets(config):
    targets = config.targets.keys()

    targets.sort()

    for target in targets:
        print(target)

    sys.exit(0)
<commit_msg>Refactoring<commit_after>from .config import parse
from .target import Target
from .__version__ import __version__

from argparse import ArgumentParser

import logging
import os
import sys

def main():
    setup_logging()

    parser = ArgumentParser(prog='construi', description='Run construi')

    parser.add_argument('target', metavar='TARGET', nargs='?')
    parser.add_argument('--basedir', metavar='DIR', default=os.getcwd())
    parser.add_argument('--version', action='version', version=__version__)
    parser.add_argument('-T', '--list-targets', action='store_true')

    args = parser.parse_args()

    config = parse(args.basedir, 'construi.yml')

    if args.list_targets:
        list_targets(config)

    target = args.target or config.default

    Target(config.for_target(target)).run()

def setup_logging():
    root_logger = logging.getLogger()
    root_logger.addHandler(logging.StreamHandler(sys.stdout))
    root_logger.setLevel(logging.INFO)

    logging.getLogger("requests").propagate = False

def list_targets(config):
    targets = config.targets.keys()

    targets.sort()

    for target in targets:
        print(target)

    sys.exit(0)
<|endoftext|>


<commit_before>__version__ = '0.1.0'
<commit_msg>bump version<commit_after>__version__ = '0.2.2'
<|endoftext|>