<commit_before>###
# Copyright (c) 2014, spline
# All rights reserved.
#
#
###
# my libs
from base64 import b64decode
import cPickle as pickle
from BeautifulSoup import BeautifulSoup
import re
# extra supybot libs
import supybot.conf as conf
import supybot.schedule as schedule
import supybot.ircmsgs as ircmsgs
# supybot libs
import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
try:
    from supybot.i18n import PluginInternationalization
    _ = PluginInternationalization('NFLDraft')
except ImportError:
    # Placeholder that allows to run the plugin on a bot
    # without the i18n module
    _ = lambda x:x

class NFLDraft(callbacks.Plugin):
    """Add the help for "@plugin help NFLDraft" here
    This should describe *how* to use this plugin."""
    threaded = True

    def __init__(self, irc):
        self.__parent = super(NFLDraft, self)
        self.__parent.__init__(irc)
        # initial states for channels.
        self.channels = {} # dict for channels with values as teams/ids
        self._loadpickle() # load saved data.
        # initial states for games.
        self.draft = None
        self.nextcheck = None
        # url for draft.
        self.url = b64decode('aHR0cDovL3d3dy5zcG90cmFjLmNvbS9kcmFmdC10cmFja2VyL25mbC8=')
        if not self.draft:
            self.draft = self._fetchdraft()
        # now schedule our events.
        def checkdraftcron():
            self.checkdraft(irc)
        try: # check scores.
            schedule.addPeriodicEvent(checkdraftcron, self.registryValue('checkInterval'), now=False, name='checkdraft')
        except AssertionError:
            try:
                schedule.removeEvent('checkdraft')
            except KeyError:
                pass
            schedule.addPeriodicEvent(checkdraftcron, self.registryValue('checkInterval'), now=False, name='checkdraft')

    def die(self):
        try:
            schedule.removeEvent('checkdraft')
        except KeyError:
            pass
        self.__parent.die()

    ######################
    # INTERNAL FUNCTIONS #
    ######################

    def _httpget(self, url):
        """General HTTP resource fetcher. Pass headers via h, data via d, and to log via l."""

        l = False
        if self.registryValue('logURLs') or l:
            self.log.info(url)

        try:
            h = {"User-Agent":"Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0"}
            page = utils.web.getUrl(url, headers=h)
            return page
        except Exception, e:
            self.log.error("_httpget: ERROR opening {0} message: {1}".format(url, e))
            return None

    ###########################################
    # INTERNAL CHANNEL POSTING AND DELEGATION #
    ###########################################

    def _post(self, irc, message):
        """Posts message to a specific channel."""

        # first check if we have channels.
        if len(self.channels) == 0: # bail if none.
            return
        # we do have channels. lets go and check where to put what.
        postchans = [k for (k, v) in self.channels.items() if v == 1] # only channels with 1 = on.
        # iterate over each and post.
        for postchan in postchans:
            try:
                irc.queueMsg(ircmsgs.privmsg(postchan, message))
            except Exception as e:
                self.log.info("_post :: ERROR :: Could not send {0} to {1}. {2}".format(message, postchan, e))

    ##############################
    # INTERNAL CHANNEL FUNCTIONS #
    ##############################

    def _loadpickle(self):
        """Load channel data from pickle."""

        try:
            datafile = open(conf.supybot.directories.data.dirize(self.name()+".pickle"), 'rb')
            try:
                dataset = pickle.load(datafile)
            finally:
                datafile.close()
        except IOError:
            return False
        # restore.
        self.channels = dataset["channels"]
        return True

    def _savepickle(self):
        """Save channel data to pickle."""

        data = {"channels": self.channels}
        try:
            datafile = open(conf.supybot.directories.data.dirize(self.name()+".pickle"), 'wb')
            try:
                pickle.dump(data, datafile)
            finally:
                datafile.close()
        except IOError:
            return False
        return True

    ####################
    # FETCH OPERATIONS #
    ####################

    def _fetchdraft(self):
        """Returns the draft stuff."""

        url = self.url
        html = self._httpget(url)
        if not html:
            self.log.error("ERROR: _fetchdraft :: could not fetch {0} :: {1}".format(url))
            return None
        # try/except for this
        try:
            soup = BeautifulSoup(html)
            # find all rows with ids that are numbers.
            plyrs = soup.findAll('tr', attrs={'id':re.compile('^\d.*?')})
            # empty container.
            d = {}
            # enumerate over these rows.
            for (i, p) in enumerate(plyrs):
                # find all tds.
                partds = p.findAll('td')
                #self.log.info("PARTDS: {0}".format(partds))
                pick = partds[0]
                pick = pick.getText().encode('utf-8')
                tm = partds[1]
                tm = tm.getText(separator=' ').encode('utf-8')
                plr = partds[2]
                plr = plr.getText().encode('utf-8')
                pos = partds[3]
                pos = pos.getText().encode('utf-8')
                # now, lets append.
                d[i] = {'pick':pick, 'plr':plr, 'pos':pos, 'tm':tm}
            # return d
            return d
        except Exception, e:
            self.log.info("_fetchdraft :: ERROR :: {0}".format(e))
            return None

    #############################
    # PUBLIC CHANNEL OPERATIONS #
    #############################

    def draftchannel(self, irc, msg, args, op, optchannel):
        """<add #channel|del #channel|list>

        Add or delete a channel from draft output.
        Use list to list channels we output to.
        Ex: add #channel OR del #channel OR list
        """

        # first, lower operation.
        op = op.lower()
        # next, make sure op is valid.
        validop = ['add', 'list', 'del']
        if op not in validop: # test for a valid operation.
            irc.reply("ERROR: '{0}' is an invalid operation. It must be be one of: {1}".format(op, " | ".join([i for i in validop])))
            return
        # if we're not doing list (add or del) make sure we have the arguments.
        if (op != 'list'):
            if not optchannel:
                irc.reply("ERROR: add and del operations require a channel and team. Ex: add #channel or del #channel")
                return
            # we are doing an add/del op.
            optchannel = optchannel.lower()
            # make sure channel is something we're in
            if optchannel not in irc.state.channels:
                irc.reply("ERROR: '{0}' is not a valid channel. You must add a channel that we are in.".format(optchannel))
                return
        # main meat part.
        # now we handle each op individually.
        if op == 'add': # add output to channel.
            self.channels[optchannel] = 1 # add it and on.
            self._savepickle() # save.
            irc.reply("I have enabled draft status updates on {0}".format(optchannel))
        elif op == 'list': # list channels
            if len(self.channels) == 0: # no channels.
                irc.reply("ERROR: I have no active channels defined. Please use the draftchannel add operation to add a channel.")
            else: # we do have channels.
                for (k, v) in self.channels.items(): # iterate through and output translated keys.
                    if v == 0: # swap 0/1 into OFF/ON.
                        irc.reply("{0} :: OFF".format(k))
                    elif v == 1:
                        irc.reply("{0} :: ON".format(k))
        elif op == 'del': # delete an item from channels.
            if optchannel in self.channels: # id is already in.
                del self.channels[optchannel] # remove it.
                self._savepickle() # save.
                irc.reply("I have successfully removed {0}".format(optchannel))
            else: # id was NOT in there.
                irc.reply("ERROR: I do not have {0} in {1}".format(optarg, optchannel))

    draftchannel = wrap(draftchannel, [('checkCapability', 'admin'), ('somethingWithoutSpaces'), optional('channel')])

    def drafton(self, irc, msg, args, channel):
        """
        Enable draft announcing in channel.
        """

        # channel
        channel = channel.lower()
        # check if op.
        if not irc.state.channels[channel].isOp(msg.nick):
            irc.reply("ERROR: You must be an op in this channel for this command to work.")
            return
        # check now.
        if channel in self.channels:
            self.channels[channel] = 1
            irc.reply("I have turned on draft livescoring for {0}".format(channel))
        else:
            irc.reply("ERROR: {0} is not in any known channels.".format(channel))

    drafton = wrap(drafton, [('channel')])

    def draftoff(self, irc, msg, args, channel):
        """
        Disable draft announcing in channel.
        """

        # channel
        channel = channel.lower()
        # check if op.
        if not irc.state.channels[channel].isOp(msg.nick):
            irc.reply("ERROR: You must be an op in this channel for this command to work.")
            return
        # check now.
        if channel in self.channels:
            self.channels[channel] = 0
            irc.reply("I have turned off draft livescoring for {0}".format(channel))
        else:
            irc.reply("ERROR: {0} is not in any known channels.".format(channel))

    draftoff = wrap(draftoff, [('channel')])

    #################
    # MAIN FUNCTION #
    #################

    #def checkdraft(self, irc, msg, args):
    def checkdraft(self, irc):
        """Main handling function."""

        self.log.info("checkdraft: starting check.")
        # first, we need a baseline set of games.
        if not self.draft: # we don't have them if reloading.
            self.log.info("checkdraft: I do not have any draft. Fetching initial draft.")
            self.draft = self._fetchdraft()
        # verify we have a baseline.
        if not self.draft: # we don't. must bail.
            self.log.info("checkdraft: after second try, I could not get self.draft.")
            return
        else: # we have games. setup the baseline stuff.
            draft1 = self.draft # games to compare from.
        # now, we must grab new games. if something goes wrong or there are None, we bail.
        draft2 = self._fetchdraft()
        if not draft2: # if something went wrong, we bail.
            self.log.info("checkdraft: I was unable to get new draft.")
            return
        # what we'll do is iterate through the list of dicts.
        # d is stored. new d is compared.
        # we iterate through each item and look if "plr" changes.
        # if plr changes, it means someone was picked.
        # we then announce the "pick" and also announce what pick/team is next.
        for (k, v) in draft1.items():  # {'rd': rd, 'pick':pick, 'plr':plr, 'col':col, 'pos':pos, 'tm':tm }
            if v['plr'] != draft2[k]['plr']:  # plr changed. that means pick is in.
                mstr = "Pick: {0} :: {1} has picked {2}, {3}".format(ircutils.bold(v['pick']), ircutils.bold(draft2[k]['tm']), ircutils.underline(draft2[k]['plr']), draft2[k]['pos'])
                self._post(irc, mstr)
                # figure out who picks next.
                nextpick = k+1  # this is the number(int) + 1.
                if nextpick > 255:  # this means the draft is over.
                    self.log.info("checkdraft: pick is {0}. we have reached the end of the draft.".format(nextpick))
                else:  # we're not at the last pick.
                    n = draft2[nextpick]  # easier to access. {'rd': rd, 'pick':pick, 'plr':plr, 'col':col, 'pos':pos, 'tm':tm }
                    self.log.info("n = {0}".format(n))
                    np = "{0} is now on the clock with the {1} pick".format(n['tm'], n['pick'])
                    self._post(irc, np)

        # now that we're done checking changes, copy the new into self.games to check against next time.
        self.draft = draft2
        self.log.info("checkdraft: done checking. copied.")

Class = NFLDraft

# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
<commit_msg>More fixes.<commit_after>###
# Copyright (c) 2014, spline
# All rights reserved.
#
#
###
# my libs
from base64 import b64decode
import cPickle as pickle
from BeautifulSoup import BeautifulSoup
import re
# extra supybot libs
import supybot.conf as conf
import supybot.schedule as schedule
import supybot.ircmsgs as ircmsgs
# supybot libs
import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
try:
    from supybot.i18n import PluginInternationalization
    _ = PluginInternationalization('NFLDraft')
except ImportError:
    # Placeholder that allows to run the plugin on a bot
    # without the i18n module
    _ = lambda x:x

class NFLDraft(callbacks.Plugin):
    """Add the help for "@plugin help NFLDraft" here
    This should describe *how* to use this plugin."""
    threaded = True

    def __init__(self, irc):
        self.__parent = super(NFLDraft, self)
        self.__parent.__init__(irc)
        # initial states for channels.
        self.channels = {} # dict for channels with values as teams/ids
        self._loadpickle() # load saved data.
        # initial states for games.
        self.draft = None
        self.nextcheck = None
        # url for draft.
        self.url = b64decode('aHR0cDovL3d3dy5zcG90cmFjLmNvbS9kcmFmdC10cmFja2VyL25mbC8=')
        if not self.draft:
            self.draft = self._fetchdraft()
        # now schedule our events.
        def checkdraftcron():
            self.checkdraft(irc)
        try: # check scores.
            schedule.addPeriodicEvent(checkdraftcron, self.registryValue('checkInterval'), now=False, name='checkdraft')
        except AssertionError:
            try:
                schedule.removeEvent('checkdraft')
            except KeyError:
                pass
            schedule.addPeriodicEvent(checkdraftcron, self.registryValue('checkInterval'), now=False, name='checkdraft')

    def die(self):
        try:
            schedule.removeEvent('checkdraft')
        except KeyError:
            pass
        self.__parent.die()

    ######################
    # INTERNAL FUNCTIONS #
    ######################

    def _httpget(self, url):
        """General HTTP resource fetcher. Pass headers via h, data via d, and to log via l."""

        l = False
        if self.registryValue('logURLs') or l:
            self.log.info(url)

        try:
            h = {"User-Agent":"Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0"}
            page = utils.web.getUrl(url, headers=h)
            return page
        except Exception, e:
            self.log.error("_httpget: ERROR opening {0} message: {1}".format(url, e))
            return None

    ###########################################
    # INTERNAL CHANNEL POSTING AND DELEGATION #
    ###########################################

    def _post(self, irc, message):
        """Posts message to a specific channel."""

        # first check if we have channels.
        if len(self.channels) == 0: # bail if none.
            return
        # we do have channels. lets go and check where to put what.
        postchans = [k for (k, v) in self.channels.items() if v == 1] # only channels with 1 = on.
        # iterate over each and post.
        for postchan in postchans:
            try:
                irc.queueMsg(ircmsgs.privmsg(postchan, message))
            except Exception as e:
                self.log.info("_post :: ERROR :: Could not send {0} to {1}. {2}".format(message, postchan, e))

    ##############################
    # INTERNAL CHANNEL FUNCTIONS #
    ##############################

    def _loadpickle(self):
        """Load channel data from pickle."""

        try:
            datafile = open(conf.supybot.directories.data.dirize(self.name()+".pickle"), 'rb')
            try:
                dataset = pickle.load(datafile)
            finally:
                datafile.close()
        except IOError:
            return False
        # restore.
        self.channels = dataset["channels"]
        return True

    def _savepickle(self):
        """Save channel data to pickle."""

        data = {"channels": self.channels}
        try:
            datafile = open(conf.supybot.directories.data.dirize(self.name()+".pickle"), 'wb')
            try:
                pickle.dump(data, datafile)
            finally:
                datafile.close()
        except IOError:
            return False
        return True

    ####################
    # FETCH OPERATIONS #
    ####################

    def _fetchdraft(self):
        """Returns the draft stuff."""

        url = self.url
        html = self._httpget(url)
        if not html:
            self.log.error("ERROR: _fetchdraft :: could not fetch {0} :: {1}".format(url))
            return None
        # try/except for this
        try:
            soup = BeautifulSoup(html)
            # find all rows with ids that are numbers.
            plyrs = soup.findAll('tr', attrs={'id':re.compile('^\d.*?')})
            # empty container.
            d = {}
            # enumerate over these rows.
            for (i, p) in enumerate(plyrs):
                # find all tds.
                partds = p.findAll('td')
                pick = partds[0]
                pick = pick.getText().encode('utf-8')
                tm = partds[1]
                tm = tm.getText(separator=' ').encode('utf-8')
                plr = partds[2]
                plr = plr.getText().encode('utf-8')
                pos = partds[3]
                pos = pos.getText().encode('utf-8')
                col = partds[4]
                col = col.getText().encode('utf-8')
                rd = partds[0].findPrevious('div', attrs={'class':'draftround'})
                rd = rd.getText().replace('Round ', '')
                # now, lets append.
                d[i] = {'pick':pick, 'plr':plr, 'pos':pos, 'tm':tm, 'col':col, 'rd':rd}
            # return d
            return d
        except Exception, e:
            self.log.info("_fetchdraft :: ERROR :: {0}".format(e))
            return None

    #############################
    # PUBLIC CHANNEL OPERATIONS #
    #############################

    def draftchannel(self, irc, msg, args, op, optchannel):
        """<add #channel|del #channel|list>

        Add or delete a channel from draft output.
        Use list to list channels we output to.
        Ex: add #channel OR del #channel OR list
        """

        # first, lower operation.
        op = op.lower()
        # next, make sure op is valid.
        validop = ['add', 'list', 'del']
        if op not in validop: # test for a valid operation.
            irc.reply("ERROR: '{0}' is an invalid operation. It must be be one of: {1}".format(op, " | ".join([i for i in validop])))
            return
        # if we're not doing list (add or del) make sure we have the arguments.
        if (op != 'list'):
            if not optchannel:
                irc.reply("ERROR: add and del operations require a channel and team. Ex: add #channel or del #channel")
                return
            # we are doing an add/del op.
            optchannel = optchannel.lower()
            # make sure channel is something we're in
            if optchannel not in irc.state.channels:
                irc.reply("ERROR: '{0}' is not a valid channel. You must add a channel that we are in.".format(optchannel))
                return
        # main meat part.
        # now we handle each op individually.
        if op == 'add': # add output to channel.
            self.channels[optchannel] = 1 # add it and on.
            self._savepickle() # save.
            irc.reply("I have enabled draft status updates on {0}".format(optchannel))
        elif op == 'list': # list channels
            if len(self.channels) == 0: # no channels.
                irc.reply("ERROR: I have no active channels defined. Please use the draftchannel add operation to add a channel.")
            else: # we do have channels.
                for (k, v) in self.channels.items(): # iterate through and output translated keys.
                    if v == 0: # swap 0/1 into OFF/ON.
                        irc.reply("{0} :: OFF".format(k))
                    elif v == 1:
                        irc.reply("{0} :: ON".format(k))
        elif op == 'del': # delete an item from channels.
            if optchannel in self.channels: # id is already in.
                del self.channels[optchannel] # remove it.
                self._savepickle() # save.
                irc.reply("I have successfully removed {0}".format(optchannel))
            else: # id was NOT in there.
                irc.reply("ERROR: I do not have {0} in {1}".format(optarg, optchannel))

    draftchannel = wrap(draftchannel, [('checkCapability', 'admin'), ('somethingWithoutSpaces'), optional('channel')])

    def drafton(self, irc, msg, args, channel):
        """
        Enable draft announcing in channel.
        """

        # channel
        channel = channel.lower()
        # check if op.
        if not irc.state.channels[channel].isOp(msg.nick):
            irc.reply("ERROR: You must be an op in this channel for this command to work.")
            return
        # check now.
        if channel in self.channels:
            self.channels[channel] = 1
            irc.reply("I have turned on draft livescoring for {0}".format(channel))
        else:
            irc.reply("ERROR: {0} is not in any known channels.".format(channel))

    drafton = wrap(drafton, [('channel')])

    def draftoff(self, irc, msg, args, channel):
        """
        Disable draft announcing in channel.
        """

        # channel
        channel = channel.lower()
        # check if op.
        if not irc.state.channels[channel].isOp(msg.nick):
            irc.reply("ERROR: You must be an op in this channel for this command to work.")
            return
        # check now.
        if channel in self.channels:
            self.channels[channel] = 0
            irc.reply("I have turned off draft livescoring for {0}".format(channel))
        else:
            irc.reply("ERROR: {0} is not in any known channels.".format(channel))

    draftoff = wrap(draftoff, [('channel')])

    #################
    # MAIN FUNCTION #
    #################

    #def checkdraft(self, irc, msg, args):
    def checkdraft(self, irc):
        """Main handling function."""

        self.log.info("checkdraft: starting check.")
        # first, we need a baseline set of games.
        if not self.draft: # we don't have them if reloading.
            self.log.info("checkdraft: I do not have any draft. Fetching initial draft.")
            self.draft = self._fetchdraft()
        # verify we have a baseline.
        if not self.draft: # we don't. must bail.
            self.log.info("checkdraft: after second try, I could not get self.draft.")
            return
        else: # we have games. setup the baseline stuff.
            draft1 = self.draft # games to compare from.
        # now, we must grab new games. if something goes wrong or there are None, we bail.
        draft2 = self._fetchdraft()
        if not draft2: # if something went wrong, we bail.
            self.log.info("checkdraft: I was unable to get new draft.")
            return
        # what we'll do is iterate through the list of dicts.
        # d is stored. new d is compared.
        # we iterate through each item and look if "plr" changes.
        # if plr changes, it means someone was picked.
        # we then announce the "pick" and also announce what pick/team is next.
        for (k, v) in draft1.items():  # {'rd': rd, 'pick':pick, 'plr':plr, 'col':col, 'pos':pos, 'tm':tm }
            if v['plr'] != draft2[k]['plr']:  # plr changed. that means pick is in.
                self.log.info("1: {0}".format(v))
                self.log.info("2: {0}".format(draft2[k]))
                mstr = "Round: {0} Pick: {1} :: {2} has picked {3}, {4}".format(v['rd'], ircutils.bold(v['pick']), ircutils.bold(draft2[k]['tm']), ircutils.underline(draft2[k]['plr']), draft2[k]['pos'])
                self._post(irc, mstr)
                # figure out who picks next.
                nextpick = k+2  # this is the number(int) + 1.
                if nextpick > 254:  # this means the draft is over.
                    self.log.info("checkdraft: pick is {0}. we have reached the end of the draft.".format(nextpick))
                else:  # we're not at the last pick.
                    n = draft2[nextpick]  # easier to access. {'rd': rd, 'pick':pick, 'plr':plr, 'col':col, 'pos':pos, 'tm':tm }
                    self.log.info("n = {0}".format(n))
                    np = "{0} is now on the clock with the {1} pick".format(n['tm'], utils.str.ordinal(nextpick))
                    self._post(irc, np)

        # now that we're done checking changes, copy the new into self.games to check against next time.
        self.draft = draft2
        self.log.info("checkdraft: done checking. copied.")

Class = NFLDraft

# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
<|endoftext|>


<commit_before>import socket, thread, select, sys, traceback, time, os
import cProfile # for profiling
from Client import Client
# from Protocol import Protocol
# from Protocol import Protocol_034 as Protocol # legacy support
import Protocol

class ClientHandler:
	'''This represents one client handler. Threading is recommended. Multiple copies work.'''
	def __init__(self, root, num):
		self.num = num
		self._root = root
		self._bind()
		self.input = []
		self.output = []
		self.socketmap = {}
		self.clients = []
		self.clients_num = 0
		self.running = False

	def _bind(self):
		self.protocol = Protocol.Protocol(self._root,self)

	def _rebind(self):
		self._bind()
		for client in self.clients:
			#client.Bind(protocol=self.protocol)
			client.Bind(protocol=Protocol.Protocol(self._root,self)) # allows client's protocol to be overridden with ease

	def Run(self):
		# commented out to remove profiling
		#if not os.path.isdir('profiling'):
		#	os.mkdir('profiling')
		#thread.start_new_thread(cProfile.runctx,('self.MainLoop()', globals(), locals(), os.path.join('profiling', '%s.log'%(self.num))))
		# normal, no profiling
		self.running = True
		thread.start_new_thread(self.MainLoop,())

	def MainLoop(self):
		while self.running:
			while not self.input: time.sleep(0.1)
			while self.running and self.input:
				try:
					try: inputready,outputready,exceptready = select.select(self.input,self.output,[], 0.5) # should I be using exceptready to close the sockets?
					except: continue
					if not self.running: continue

					for s in inputready:
						try:
							data = s.recv(1024)
						except socket.error:
							self._remove(s)
							continue
						if data:
							if s in self.socketmap:
								self.socketmap[s].Handle(data)
						else:
							self._remove(s)

					for s in outputready:
						try:
							self.socketmap[s].FlushBuffer()
						except KeyError:
							self._remove(s)
						except socket.error:
							s.close()
							self._remove(s)
				except:	self._root.error(traceback.format_exc())
		self.running = False

	def _remove(self,s):
		if s in self.input:
			self.input.remove(s)
		if s in self.output:
			self.output.remove(s)
		if s in self.socketmap: # called twice sometimes, so needs the check
			client = self.socketmap[s]
			client.Remove()
			try: del self.socketmap[s]
			except: pass

	def AddClient(self, client):
		self.clients_num += 1
		self.socketmap[client.conn] = client

		self.clients.append(client)
		client.Bind(self, self.protocol)
		if not self.running: self.Run()

	def RemoveClient(self, client, reason='Quit'):
		self.clients_num -= 1
		while client.conn in self.input:
			self.input.remove(client.conn)
 		while client.conn in self.output:
			self.output.remove(client.conn)
		if client in self.clients:
			self.clients.remove(client)
		self._root.console_write('Client disconnected from %s, session ID was %s'%(client.ip_address, client.session_id))
		client._protocol._remove(client, reason)<commit_msg>added another handler duplicity check<commit_after>import socket, thread, select, sys, traceback, time, os
import cProfile # for profiling
from Client import Client
# from Protocol import Protocol
# from Protocol import Protocol_034 as Protocol # legacy support
import Protocol

class ClientHandler:
	'''This represents one client handler. Threading is recommended. Multiple copies work.'''
	def __init__(self, root, num):
		self.num = num
		self._root = root
		self._bind()
		self.input = []
		self.output = []
		self.socketmap = {}
		self.clients = []
		self.clients_num = 0
		self.running = False

	def _bind(self):
		self.protocol = Protocol.Protocol(self._root,self)

	def _rebind(self):
		self._bind()
		for client in self.clients:
			#client.Bind(protocol=self.protocol)
			client.Bind(protocol=Protocol.Protocol(self._root,self)) # allows client's protocol to be overridden with ease

	def Run(self):
		if self.running: return
		# commented out to remove profiling
		#if not os.path.isdir('profiling'):
		#	os.mkdir('profiling')
		#thread.start_new_thread(cProfile.runctx,('self.MainLoop()', globals(), locals(), os.path.join('profiling', '%s.log'%(self.num))))
		# normal, no profiling
		self.running = True
		thread.start_new_thread(self.MainLoop,())

	def MainLoop(self):
		while self.running:
			while not self.input: time.sleep(0.1)
			while self.running and self.input:
				try:
					try: inputready,outputready,exceptready = select.select(self.input,self.output,[], 0.5) # should I be using exceptready to close the sockets?
					except: continue
					if not self.running: continue

					for s in inputready:
						try:
							data = s.recv(1024)
						except socket.error:
							self._remove(s)
							continue
						if data:
							if s in self.socketmap:
								self.socketmap[s].Handle(data)
						else:
							self._remove(s)

					for s in outputready:
						try:
							self.socketmap[s].FlushBuffer()
						except KeyError:
							self._remove(s)
						except socket.error:
							s.close()
							self._remove(s)
				except:	self._root.error(traceback.format_exc())
		self.running = False

	def _remove(self,s):
		if s in self.input:
			self.input.remove(s)
		if s in self.output:
			self.output.remove(s)
		if s in self.socketmap: # called twice sometimes, so needs the check
			client = self.socketmap[s]
			client.Remove()
			try: del self.socketmap[s]
			except: pass

	def AddClient(self, client):
		self.clients_num += 1
		self.socketmap[client.conn] = client

		self.clients.append(client)
		client.Bind(self, self.protocol)
		if not self.running: self.Run()

	def RemoveClient(self, client, reason='Quit'):
		self.clients_num -= 1
		while client.conn in self.input:
			self.input.remove(client.conn)
 		while client.conn in self.output:
			self.output.remove(client.conn)
		if client in self.clients:
			self.clients.remove(client)
		self._root.console_write('Client disconnected from %s, session ID was %s'%(client.ip_address, client.session_id))
		client._protocol._remove(client, reason)<|endoftext|>


<commit_before>import asyncio, discord
from discord.ext import commands
from Cogs import Utils, DisplayName, Message

def setup(bot):
    bot.add_cog(Lockdown(bot, bot.get_cog("Settings")))

class Lockdown(commands.Cog):

    # Init with the bot reference, and a reference to the settings var
    def __init__(self, bot, settings):
        self.bot = bot
        self.settings = settings
        self.lockdown_perms = ("send_messages","add_reactions","speak")
        global Utils, DisplayName
        Utils = self.bot.get_cog("Utils")
        DisplayName = self.bot.get_cog("DisplayName")

    def _get_lockdown(self, ctx):
        # Returns a tuple of (lockdown_ids, channel_objects)
        lockdown = self.settings.getServerStat(ctx.guild,"LockdownList",[])
        if not len(lockdown): return ([],None)
        orphaned,channels = self._verify_lockdown(ctx,lockdown)
        if len(orphaned): # Update the lockdown list to reflect changes
            lockdown = [x for x in lockdown if not x in orphaned]
            self.settings.setServerStat(ctx.guild,"LockdownList",lockdown)
        return (lockdown,channels)

    def _verify_lockdown(self, ctx, lockdown):
        # Returns a tuple of (orphaned_ids, channel_objects)
        channels = []
        orphaned = []
        if not isinstance(lockdown,(list,tuple)): lockdown = (lockdown,)
        for c in lockdown:
            channel = ctx.guild.get_channel(int(c))
            if not channel: orphaned.append(c)
            else: channels.append(channel)
        # Let's get the guild by_category() output and sort our channels using that
        ordered = []
        for cat,chan_list in ctx.guild.by_category():
            if cat and not cat.id in ordered: ordered.append(cat.id)
            ordered.extend([chan.id for chan in chan_list])
        # Return the list sorted by discord's GUI position
        return (orphaned,sorted(channels,key=lambda x:ordered.index(x.id)))

    def _get_mention(self,channel):
        # Returns a formatted mention for the passed channel - including
        # the number of synced channels if it's a category
        if isinstance(channel,discord.CategoryChannel):
            synced = [x for x in channel.channels if x.permissions_synced] if hasattr(channel,"permissions_synced") else channel.channels
            return "{} ({:,}/{:,} synced)".format(
                channel.mention,
                len(synced),
                len(channel.channels)
            )
        return "{}{}".format("☑ " if not hasattr(channel,"permissions_synced") or channel.permissions_synced else "", channel.mention)

    async def _check_lockdown(self, lockdown, ctx):
        # Helper to auto-reply if Lockdown is not configured
        if len(lockdown): return True
        await Message.EmbedText(
            title="Lockdown is not configured!",
            description="You can add channels/categories to the lockdown list with `{}addlock [channel list]`".format(ctx.prefix),
            color=ctx.author
        ).send(ctx)
        return False

    async def _perform_lockdown(self, ctx, target_channels, unlock=False):
        # Helper to lock or unlock channels based on the current context.
        guild = ctx if isinstance(ctx,discord.Guild) else ctx.guild if isinstance(ctx,discord.ext.commands.Context) else None
        if guild is None: return (0,0) # Got sent some wonky values, I guess...
        default_role = guild.default_role
        lockdown = [x for x in target_channels] # Duplicate the list to avoid removing from the original
        categories = channels = 0
        while len(lockdown):
            channel = lockdown.pop(0)
            # Increment counts
            if isinstance(channel,discord.CategoryChannel):
                categories += 1
                # Add all missing channels that do not have synced perms
                new_chans = [x for x in channel.channels if not x in lockdown and (not hasattr(channel,"permissions_synced") or not x.permissions_synced)]
                lockdown.extend(new_chans)
            else:
                channels += 1
                # Check if we even need to update perms based on syncing/category inclusion
                if channel.category and hasattr(channel,"permissions_synced") and channel.permissions_synced and channel.category in target_channels: continue
            overs = channel.overwrites_for(default_role) # Get any overrides for the role
            # Check if we qualify in this channel to (un)lockdown
            if unlock: perm_check  = any(x[0] in self.lockdown_perms and x[1] != None for x in overs)
            else: perm_check = not all([x==False for x in (overs.send_messages,overs.add_reactions,overs.speak)])
            if perm_check: # We qualify - set our perms as needed
                other_perms = any(x[0] not in self.lockdown_perms and x[1] != None for x in overs)
                overs.send_messages = overs.add_reactions = overs.speak = None if unlock else False
                try: await channel.set_permissions(default_role, overwrite=overs if other_perms or not unlock else None)
                except: pass
        return (categories,channels)

    @commands.command()
    async def listlock(self, ctx):
        """Lists the channels and categories configured for lockdown (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        lockdown,channels = self._get_lockdown(ctx)
        if not await self._check_lockdown(lockdown,ctx): return
        desc = "\n".join([self._get_mention(x) for x in channels])
        await Message.EmbedText(title="Current Lockdown List - {:,} Total".format(len(lockdown)),description=desc,color=ctx.author).send(ctx)

    @commands.command()
    async def checklock(self, ctx):
        """Reports the number of configured channels that are locked down (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        lockdown,channels = self._get_lockdown(ctx)
        if not await self._check_lockdown(lockdown,ctx): return
        message = await Message.EmbedText(
            title="Checking Lockdown",
            description="Checking {:,} entr{}...".format(len(lockdown),"y" if len(lockdown)==1 else "ies"),
            color=ctx.author
        ).send(ctx)
        default_role = ctx.guild.default_role
        locked = []
        for channel in channels:
            overs = channel.overwrites_for(default_role)
            if all([x==False for x in (overs.send_messages,overs.add_reactions,overs.speak)]):
                locked.append(channel)
        await Message.EmbedText(
            title="Lockdown Status: {}".format("UNLOCKED" if len(locked) == 0 else "LOCKED" if len(locked)==len(lockdown) else "PARTIALLY LOCKED"),
            description="{:,}/{:,} entr{} fully locked down.".format(len(locked),len(lockdown),"y" if len(lockdown)==1 else "ies")
        ).edit(ctx,message)

    @commands.command()
    async def addlock(self, ctx, *, channel_list = None):
        """Adds the passed space-delimited list of channels and categories to the lockdown list (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        lockdown,channels = self._get_lockdown(ctx)
        if not channel_list: return await ctx.send("Usage: `{}addlock [channel list]`".format(ctx.prefix))
        resolved = []
        resolved_id = []
        for channel in channel_list.split():
            c = DisplayName.channelForName(channel,ctx.guild)
            if c and not c.id in lockdown:
                resolved.append(c)
                resolved_id.append(c.id)
            # Also consider child channels as needed
            if isinstance(c,discord.CategoryChannel):
                for c_child in c.channels:
                    if not c_child.id in lockdown:
                        resolved.append(c_child)
                        resolved_id.append(c_child.id)
        if not len(resolved): return await ctx.send("No valid channels passed!\nUsage: `{}addlock [channel list]`".format(ctx.prefix))
        lockdown.extend(resolved_id)
        self.settings.setServerStat(ctx.guild,"LockdownList",lockdown)
        desc = "\n".join([self._get_mention(x) for x in resolved])
        await Message.EmbedText(title="{:,} Added to Lockdown List".format(len(resolved)),description=desc,color=ctx.author).send(ctx)

    @commands.command()
    async def addlockall(self, ctx):
        """Adds all channels and categories to the lockdown list (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        orphaned,channels = self._verify_lockdown(ctx,[x.id for x in ctx.guild.channels])
        if not len(channels): return await ctx.send("No valid channels found!")
        lockdown = [x.id for x in channels]
        self.settings.setServerStat(ctx.guild,"LockdownList",lockdown)
        desc = "\n".join([self._get_mention(x) for x in channels])
        await Message.EmbedText(title="{:,} Added to Lockdown List".format(len(lockdown)),description=desc,color=ctx.author).send(ctx)

    @commands.command()
    async def remlock(self, ctx, *, channel_list = None):
        """Removes the passed space-delimited list of channels and categories from the lockdown list (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        lockdown,channels = self._get_lockdown(ctx)
        if not await self._check_lockdown(lockdown,ctx): return
        if not channel_list: return await ctx.send("Usage: `{}remlock [channel list]`".format(ctx.prefix))
        resolved = []
        resolved_id = []
        for channel in channel_list.split():
            c = DisplayName.channelForName(channel,ctx.guild)
            if c and c.id in lockdown:
                resolved.append(c)
                resolved_id.append(c.id)
            # Also consider child channels as needed
            if isinstance(c,discord.CategoryChannel):
                for c_child in c.channels:
                    if c_child and c_child.id in lockdown:
                        resolved.append(c_child)
                        resolved_id.append(c_child.id)
        if not len(resolved): return await ctx.send("No valid channels passed!\nUsage: `{}remlock [channel list]`".format(ctx.prefix))
        lockdown = [x for x in lockdown if not x in resolved_id]
        self.settings.setServerStat(ctx.guild,"LockdownList",lockdown)
        desc = "\n".join([self._get_mention(x) for x in resolved])
        await Message.EmbedText(title="{:,} Removed from Lockdown List".format(len(resolved)),description=desc,color=ctx.author).send(ctx)

    @commands.command()
    async def remlockall(self, ctx):
        """Removes all channels and categories from the lockdown list (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        lockdown,channels = self._get_lockdown(ctx)
        if not await self._check_lockdown(lockdown,ctx): return
        self.settings.setServerStat(ctx.guild,"LockdownList",[])
        desc = "\n".join([self._get_mention(x) for x in channels])
        await Message.EmbedText(title="{:,} Removed from Lockdown List".format(len(lockdown)),description=desc,color=ctx.author).send(ctx)

    @commands.command()
    async def lockdown(self, ctx, target_channel = None):
        """Iterate through the channels in the lockdown list and revoke the send_message, add_reaction, and speak permissions for the everyone role (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        if target_channel:
            c = DisplayName.channelForName(target_channel,ctx.guild)
            if not c: return await ctx.send("The passed value is not a valid channel or category!")
            orphaned,channels = self._verify_lockdown(ctx,c.id)
            lockdown = channels
        else:
            lockdown,channels = self._get_lockdown(ctx)
            if not await self._check_lockdown(lockdown,ctx): return
        message = await Message.EmbedText(
            title="Lockdown",
            description="Locking down {:,} entr{}...".format(len(lockdown),"y" if len(lockdown)==1 else "ies"),
            color=ctx.author
        ).send(ctx)
        cats,chans = await self._perform_lockdown(ctx,channels)
        if cats==chans==0: return await Message.EmbedText(title="Lockdown",description="**LOCKDOWN FAILED!**").edit(ctx,message)
        await Message.EmbedText(
            title="Lockdown",
            description="Locked down {}{}.".format(
                "{:,} categor{}".format(cats,"y" if cats==1 else "ies") if cats else "",
                "" if not chans else "{}{:,} channel{}.".format(", " if cats else "",chans,"" if chans==1 else "s")
            )
        ).edit(ctx,message)

    @commands.command()
    async def unlockdown(self, ctx, target_channel = None):
        """Iterate through the channels in the lockdown list and clear the send_message, add_reaction, and speak permissions for the everyone role (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        if target_channel:
            c = DisplayName.channelForName(target_channel,ctx.guild)
            if not c: return await ctx.send("The passed value is not a valid channel or category!")
            orphaned,channels = self._verify_lockdown(ctx,c.id)
            lockdown = channels
        else:
            lockdown,channels = self._get_lockdown(ctx)
            if not await self._check_lockdown(lockdown,ctx): return
        message = await Message.EmbedText(
            title="Unlockdown",
            description="Unlocking {:,} entr{}...".format(len(lockdown),"y" if len(lockdown)==1 else "ies"),
            color=ctx.author
        ).send(ctx)
        cats,chans = await self._perform_lockdown(ctx,channels,unlock=True)
        if cats==chans==0: return await Message.EmbedText(title="Unlockdown",description="**UNLOCKDOWN FAILED!**").edit(ctx,message)
        await Message.EmbedText(
            title="Unlockdown",
            description="Unlocked {}{}.".format(
                "{:,} categor{}".format(cats,"y" if cats==1 else "ies") if cats else "",
                "" if not chans else "{}{:,} channel{}.".format(", " if cats else "",chans,"" if chans==1 else "s")
            )
        ).edit(ctx,message)
<commit_msg>Expand output<commit_after>import asyncio, discord
from discord.ext import commands
from Cogs import Utils, DisplayName, Message

def setup(bot):
    bot.add_cog(Lockdown(bot, bot.get_cog("Settings")))

class Lockdown(commands.Cog):

    # Init with the bot reference, and a reference to the settings var
    def __init__(self, bot, settings):
        self.bot = bot
        self.settings = settings
        self.lockdown_perms = ("send_messages","add_reactions","speak")
        global Utils, DisplayName
        self.key = "Key: 🔴=Locked 🟡=Partial Lock 🟢=Unlocked 🔄=Synced 🟦=Not Synced"
        self.key_long = self.key+" ✅=Configured 🟩=Not Configured"
        Utils = self.bot.get_cog("Utils")
        DisplayName = self.bot.get_cog("DisplayName")

    def _get_lockdown(self, ctx):
        # Returns a tuple of (lockdown_ids, channel_objects)
        lockdown = self.settings.getServerStat(ctx.guild,"LockdownList",[])
        if not len(lockdown): return ([],None)
        orphaned,channels = self._verify_lockdown(ctx,lockdown)
        if len(orphaned): # Update the lockdown list to reflect changes
            lockdown = [x for x in lockdown if not x in orphaned]
            self.settings.setServerStat(ctx.guild,"LockdownList",lockdown)
        return (lockdown,channels)

    def _verify_lockdown(self, ctx, lockdown):
        # Returns a tuple of (orphaned_ids, channel_objects)
        channels = []
        orphaned = []
        if not isinstance(lockdown,(list,tuple)): lockdown = (lockdown,)
        for c in lockdown:
            channel = ctx.guild.get_channel(int(c))
            if not channel: orphaned.append(c)
            else: channels.append(channel)
        # Let's get the guild by_category() output and sort our channels using that
        ordered = []
        for cat,chan_list in ctx.guild.by_category():
            if cat and not cat.id in ordered: ordered.append(cat.id)
            ordered.extend([chan.id for chan in chan_list])
        # Return the list sorted by discord's GUI position
        return (orphaned,sorted(channels,key=lambda x:ordered.index(x.id)))

    def _get_mention(self,channel,show_lock=False,lockdown_list=[]):
        # Returns a formatted mention for the passed channel - including
        # the number of synced channels if it's a category
        #
        # First we get the overrides for the default role to check if this channel is
        # locked, unlocked, or partially locked
        default_role = channel.guild.default_role
        overs = channel.overwrites_for(default_role)
        overs_check = (overs.send_messages,overs.add_reactions,overs.speak)
        lock = "🔴" if all([x==False for x in overs_check]) else "🟡" if any([x==False for x in overs_check]) else "🟢"
        lock_text = ""
        if show_lock: lock_text = "✅ " if channel.id in lockdown_list else "🟩 "
        if isinstance(channel,discord.CategoryChannel):
            synced = [x for x in channel.channels if x.permissions_synced] if hasattr(channel,"permissions_synced") else channel.channels
            return "{}{} {} ({:,}/{:,} synced)".format(
                lock_text,
                lock,
                channel.name,
                len(synced),
                len(channel.channels)
            )
        return "{}{} {}{}".format(lock_text,lock, "🔄 " if not hasattr(channel,"permissions_synced") or channel.permissions_synced else "🟦 " if channel.category else "", channel.mention)

    async def _check_lockdown(self, lockdown, ctx):
        # Helper to auto-reply if Lockdown is not configured
        if len(lockdown): return True
        await Message.EmbedText(
            title="Lockdown is not configured!",
            description="You can add channels/categories to the lockdown list with `{}addlock [channel list]`".format(ctx.prefix),
            color=ctx.author
        ).send(ctx)
        return False

    async def _perform_lockdown(self, ctx, target_channels, unlock=False):
        # Helper to lock or unlock channels based on the current context.
        guild = ctx if isinstance(ctx,discord.Guild) else ctx.guild if isinstance(ctx,discord.ext.commands.Context) else None
        if guild is None: return (0,0) # Got sent some wonky values, I guess...
        default_role = guild.default_role
        lockdown = [x for x in target_channels] # Duplicate the list to avoid removing from the original
        categories = channels = 0
        while len(lockdown):
            channel = lockdown.pop(0)
            # Increment counts
            if isinstance(channel,discord.CategoryChannel):
                categories += 1
                # Add all missing channels that do not have synced perms
                new_chans = [x for x in channel.channels if not x in lockdown and (not hasattr(channel,"permissions_synced") or not x.permissions_synced)]
                lockdown.extend(new_chans)
            else:
                channels += 1
                # Check if we even need to update perms based on syncing/category inclusion
                if channel.category and hasattr(channel,"permissions_synced") and channel.permissions_synced and channel.category in target_channels: continue
            overs = channel.overwrites_for(default_role) # Get any overrides for the role
            # Check if we qualify in this channel to (un)lockdown
            if unlock: perm_check  = any(x[0] in self.lockdown_perms and x[1] != None for x in overs)
            else: perm_check = not all([x==False for x in (overs.send_messages,overs.add_reactions,overs.speak)])
            if perm_check: # We qualify - set our perms as needed
                other_perms = any(x[0] not in self.lockdown_perms and x[1] != None for x in overs)
                overs.send_messages = overs.add_reactions = overs.speak = None if unlock else False
                try: await channel.set_permissions(default_role, overwrite=overs if other_perms or not unlock else None)
                except: pass
        return (categories,channels)

    @commands.command()
    async def listlock(self, ctx):
        """Lists the channels and categories configured for lockdown (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        lockdown,channels = self._get_lockdown(ctx)
        if not await self._check_lockdown(lockdown,ctx): return
        desc = "\n".join([self._get_mention(x) for x in channels])
        await Message.EmbedText(title="Current Lockdown List - {:,} Total".format(len(lockdown)),description=desc,color=ctx.author,footer=self.key).send(ctx)

    @commands.command()
    async def listlockall(self, ctx):
        """Lists all channels and categories and their lockdown/sync status (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        lockdown = self.settings.getServerStat(ctx.guild,"LockdownList",[])
        desc = "\n".join([self._get_mention(x,lockdown_list=lockdown,show_lock=True) for x in ctx.guild.channels])
        await Message.EmbedText(title="All Channel Lockdown Status - {:,} Total".format(len(ctx.guild.channels)),description=desc,color=ctx.author,footer=self.key_long).send(ctx)

    @commands.command()
    async def addlock(self, ctx, *, channel_list = None):
        """Adds the passed space-delimited list of channels and categories to the lockdown list (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        lockdown,channels = self._get_lockdown(ctx)
        if not channel_list: return await ctx.send("Usage: `{}addlock [channel list]`".format(ctx.prefix))
        resolved = []
        resolved_id = []
        for channel in channel_list.split():
            c = DisplayName.channelForName(channel,ctx.guild)
            if c and not c.id in lockdown:
                resolved.append(c)
                resolved_id.append(c.id)
            # Also consider child channels as needed
            if isinstance(c,discord.CategoryChannel):
                for c_child in c.channels:
                    if not c_child.id in lockdown:
                        resolved.append(c_child)
                        resolved_id.append(c_child.id)
        if not len(resolved): return await ctx.send("No valid channels passed!\nUsage: `{}addlock [channel list]`".format(ctx.prefix))
        lockdown.extend(resolved_id)
        self.settings.setServerStat(ctx.guild,"LockdownList",lockdown)
        desc = "\n".join([self._get_mention(x) for x in resolved])
        await Message.EmbedText(title="{:,} New Entr{} Added to Lockdown List".format(len(resolved),"y" if len(resolved)==1 else "ies"),description=desc,color=ctx.author,footer=self.key).send(ctx)

    @commands.command()
    async def addlockall(self, ctx):
        """Adds all channels and categories to the lockdown list (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        orphaned,channels = self._verify_lockdown(ctx,[x.id for x in ctx.guild.channels])
        if not len(channels): return await ctx.send("No valid channels found!")
        lockdown = [x.id for x in channels]
        new_lockdown = [x for x in channels if not x.id in self.settings.getServerStat(ctx.guild,"LockdownList",[])]
        self.settings.setServerStat(ctx.guild,"LockdownList",lockdown)
        desc = "\n".join([self._get_mention(x) for x in new_lockdown])
        await Message.EmbedText(title="{:,} New Entr{} Added to Lockdown List".format(len(new_lockdown),"y" if len(new_lockdown)==1 else "ies"),description=desc,color=ctx.author,footer=self.key).send(ctx)

    @commands.command()
    async def remlock(self, ctx, *, channel_list = None):
        """Removes the passed space-delimited list of channels and categories from the lockdown list (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        lockdown,channels = self._get_lockdown(ctx)
        if not await self._check_lockdown(lockdown,ctx): return
        if not channel_list: return await ctx.send("Usage: `{}remlock [channel list]`".format(ctx.prefix))
        resolved = []
        resolved_id = []
        for channel in channel_list.split():
            c = DisplayName.channelForName(channel,ctx.guild)
            if c and c.id in lockdown:
                resolved.append(c)
                resolved_id.append(c.id)
            # Also consider child channels as needed
            if isinstance(c,discord.CategoryChannel):
                for c_child in c.channels:
                    if c_child and c_child.id in lockdown:
                        resolved.append(c_child)
                        resolved_id.append(c_child.id)
        if not len(resolved): return await ctx.send("No valid channels passed!\nUsage: `{}remlock [channel list]`".format(ctx.prefix))
        lockdown = [x for x in lockdown if not x in resolved_id]
        self.settings.setServerStat(ctx.guild,"LockdownList",lockdown)
        desc = "\n".join([self._get_mention(x) for x in resolved])
        await Message.EmbedText(title="{:,} Entr{} Removed from Lockdown List".format(len(resolved),"y" if len(resolved)==1 else "ies"),description=desc,color=ctx.author,footer=self.key).send(ctx)

    @commands.command()
    async def remlockall(self, ctx):
        """Removes all channels and categories from the lockdown list (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        lockdown,channels = self._get_lockdown(ctx)
        if not await self._check_lockdown(lockdown,ctx): return
        self.settings.setServerStat(ctx.guild,"LockdownList",[])
        desc = "\n".join([self._get_mention(x) for x in channels])
        await Message.EmbedText(title="{:,} Entr{} Removed from Lockdown List".format(len(lockdown),"y" if len(lockdown)==1 else "ies"),description=desc,color=ctx.author,footer=self.key).send(ctx)

    @commands.command()
    async def lockdown(self, ctx, target_channel = None):
        """Iterate through the channels in the lockdown list and revoke the send_message, add_reaction, and speak permissions for the everyone role (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        if target_channel:
            c = DisplayName.channelForName(target_channel,ctx.guild)
            if not c: return await ctx.send("The passed value is not a valid channel or category!")
            orphaned,channels = self._verify_lockdown(ctx,c.id)
            lockdown = channels
        else:
            lockdown,channels = self._get_lockdown(ctx)
            if not await self._check_lockdown(lockdown,ctx): return
        message = await Message.EmbedText(
            title="Lockdown",
            description="🔴 Locking down {:,} entr{}...".format(len(lockdown),"y" if len(lockdown)==1 else "ies"),
            color=ctx.author
        ).send(ctx)
        cats,chans = await self._perform_lockdown(ctx,channels)
        if cats==chans==0: return await Message.EmbedText(title="Lockdown",description="**LOCKDOWN FAILED!**").edit(ctx,message)
        await Message.EmbedText(
            title="Lockdown",
            description="🔴 Locked down {}{}.".format(
                "{:,} categor{}".format(cats,"y" if cats==1 else "ies") if cats else "",
                "" if not chans else "{}{:,} channel{}.".format(", " if cats else "",chans,"" if chans==1 else "s")
            )
        ).edit(ctx,message)

    @commands.command()
    async def unlockdown(self, ctx, target_channel = None):
        """Iterate through the channels in the lockdown list and clear the send_message, add_reaction, and speak permissions for the everyone role (bot-admin only)."""
        if not await Utils.is_bot_admin_reply(ctx): return
        if target_channel:
            c = DisplayName.channelForName(target_channel,ctx.guild)
            if not c: return await ctx.send("The passed value is not a valid channel or category!")
            orphaned,channels = self._verify_lockdown(ctx,c.id)
            lockdown = channels
        else:
            lockdown,channels = self._get_lockdown(ctx)
            if not await self._check_lockdown(lockdown,ctx): return
        message = await Message.EmbedText(
            title="Unlockdown",
            description="🟢 Unlocking {:,} entr{}...".format(len(lockdown),"y" if len(lockdown)==1 else "ies"),
            color=ctx.author
        ).send(ctx)
        cats,chans = await self._perform_lockdown(ctx,channels,unlock=True)
        if cats==chans==0: return await Message.EmbedText(title="Unlockdown",description="**UNLOCKDOWN FAILED!**").edit(ctx,message)
        await Message.EmbedText(
            title="Unlockdown",
            description="🟢 Unlocked {}{}.".format(
                "{:,} categor{}".format(cats,"y" if cats==1 else "ies") if cats else "",
                "" if not chans else "{}{:,} channel{}.".format(", " if cats else "",chans,"" if chans==1 else "s")
            )
        ).edit(ctx,message)
<|endoftext|>


<commit_before>from os import environ
from sys import stderr
from updot import ln, mkdir, platform

# import argparse, argh, see
#   https://chase-seibert.github.io/blog/2014/03/21/python-multilevel-argparse.html
# import https://github.com/dbarnett/python-selfcompletion or argcomplete

# global options:
#   what-if
#   stop-on-first

def error(text):
    stderr.write(text)

PROJ                 = 'c:/proj'
APPDATA              = environ["APPDATA"]
SUBLIME_PACKAGE_ROOT = f'{APPDATA}/Sublime Text 3/Packages'

mkdir('~/bin')
mkdir('~/go/bin')

ln('~/dotfiles/config', '~/.config')
ln('~/Common/Private', '~/dotfiles/private')

mkdir('~/.ssh')
ln('~/dotfiles/private/ssh/authorized_keys', '~/.ssh/authorized_keys')

ln('~/.config/tmux/tmux.conf', '~/.tmux.conf') # tmux refuses to support xdg (https://github.com/tmux/tmux/issues/142)
ln('~/.config/pdb/pdbrc.py', '~/.pdbrc.py')    # pdbpp uses fancycompleter which hard codes ~/<configname> and doesn't do xdg

ln(f'{PROJ}/unity-meta', '~/unity-meta')

if platform.POSIX:
    touch('~/.hushlogin')

if platform.TERMUX:
    ln('~/.config/termux', '~/.termux')

if platform.WSL:
    ln('/mnt/c', '/c')

if platform.CYGWIN:
    ln('~/dotfiles/special/cygwin/profile', '~/.profile')
    ln('~/dotfiles/special/cygwin/minttyrc', '~/.minttyrc')
    ln('/cygdrive/c', '/c')

if platform.WINDOWS:
    ln('~/.config/git/config-windows',                 '~/.gitconfig')
    ln('~/.config/omnisharp',                          '~/.omnisharp')
    ln('~/dotfiles/special/vscode/User',              f'{APPDATA}/Code/User')
    ln('~/dotfiles/private/openvpn/config',            '~/OpenVPN/config')

    ln('~/Games/Factorio',                            f'{APPDATA}/Factorio')

    ln('~/Common/_Settings/gimp-2.8',                  '.gimp-2.8')
    ln('~/Common/_Settings/Ssh',                       '.ssh')
    ln('~/Common/_Settings/minttyrc.txt',              '.minttyrc')
    ln('~/Common/Visual Studio 2013',                  'Documents/Visual Studio 2013')
    ln('~/Common/Visual Studio 2015',                  'Documents/Visual Studio 2015')
    ln('~/Common/Visual Studio 2017',                  'Documents/Visual Studio 2017')
    ln('~/Common/WindowsPowerShell',                   'Documents/WindowsPowerShell')

    ln('~/unity-meta/Perforce Jam Language Files',    f'{SUBLIME_PACKAGE_ROOT}/Perforce Jam Language Files')
    ln('~/unity-meta/Unity bindings',                 f'{SUBLIME_PACKAGE_ROOT}/Unity bindings')

# registry

# vs code extensions

# visual studio keyboard settings

# parts of mc.ini and elinks configs that make sense to share
<commit_msg>These old VS's are dead to me<commit_after>from os import environ
from sys import stderr
from updot import ln, mkdir, platform

# import argparse, argh, see
#   https://chase-seibert.github.io/blog/2014/03/21/python-multilevel-argparse.html
# import https://github.com/dbarnett/python-selfcompletion or argcomplete

# global options:
#   what-if
#   stop-on-first

def error(text):
    stderr.write(text)

PROJ                 = 'c:/proj'
APPDATA              = environ["APPDATA"]
SUBLIME_PACKAGE_ROOT = f'{APPDATA}/Sublime Text 3/Packages'

mkdir('~/bin')
mkdir('~/go/bin')

ln('~/dotfiles/config', '~/.config')
ln('~/Common/Private', '~/dotfiles/private')

mkdir('~/.ssh')
ln('~/dotfiles/private/ssh/authorized_keys', '~/.ssh/authorized_keys')

ln('~/.config/tmux/tmux.conf', '~/.tmux.conf') # tmux refuses to support xdg (https://github.com/tmux/tmux/issues/142)
ln('~/.config/pdb/pdbrc.py', '~/.pdbrc.py')    # pdbpp uses fancycompleter which hard codes ~/<configname> and doesn't do xdg

ln(f'{PROJ}/unity-meta', '~/unity-meta')

if platform.POSIX:
    touch('~/.hushlogin')

if platform.TERMUX:
    ln('~/.config/termux', '~/.termux')

if platform.WSL:
    ln('/mnt/c', '/c')

if platform.CYGWIN:
    ln('~/dotfiles/special/cygwin/profile', '~/.profile')
    ln('~/dotfiles/special/cygwin/minttyrc', '~/.minttyrc')
    ln('/cygdrive/c', '/c')

if platform.WINDOWS:
    ln('~/.config/git/config-windows',                 '~/.gitconfig')
    ln('~/.config/omnisharp',                          '~/.omnisharp')
    ln('~/dotfiles/special/vscode/User',              f'{APPDATA}/Code/User')
    ln('~/dotfiles/private/openvpn/config',            '~/OpenVPN/config')

    ln('~/Games/Factorio',                            f'{APPDATA}/Factorio')

    ln('~/Common/_Settings/gimp-2.8',                  '.gimp-2.8')
    ln('~/Common/_Settings/Ssh',                       '.ssh')
    ln('~/Common/_Settings/minttyrc.txt',              '.minttyrc')
    ln('~/Common/Visual Studio 2017',                  'Documents/Visual Studio 2017')
    ln('~/Common/WindowsPowerShell',                   'Documents/WindowsPowerShell')

    ln('~/unity-meta/Perforce Jam Language Files',    f'{SUBLIME_PACKAGE_ROOT}/Perforce Jam Language Files')
    ln('~/unity-meta/Unity bindings',                 f'{SUBLIME_PACKAGE_ROOT}/Unity bindings')

# registry

# vs code extensions

# visual studio keyboard settings

# parts of mc.ini and elinks configs that make sense to share
<|endoftext|>


<commit_before>import csv
import sys
import re

old_fn = sys.argv[1]
new_fn = sys.argv[2]

class DicLine:
    def __init__(self, en, hu, la, pl, key, status="u", pos="N", definition="TODO", comment="%"):
        self.en = en
        self.hu = hu
        self.la = la
        self.pl = pl
        self.key = key
        self.status = status
        self.pos = pos
        self.definition = definition
        if comment == "" or not re.match(r'%', comment):
            self.comment = "%" + comment
        else:
            self.comment = comment

    def __str__(self):
        return '\t'.join([self.en, self.hu, self.la, self.pl, self.key, self.status, self.pos, self.definition, self.comment])

old_lines = {}
with open(old_fn) as old_file:
    csv_reader = csv.reader(old_file, delimiter='\t')
    for row in csv_reader:
        curline = DicLine(*row[:9])
        if curline.key in old_lines:
            raise ValueError(f'{curline.key} is already in use!')
        old_lines[curline.key] = curline

def syntax_error(entry):
    return (
        not re.match(r'^(\w|[#-])*$', entry.en) or
        not re.match(r'^(\w|[#-])*$', entry.pl) or
        not re.match(r'^(\w|[#-])*$', entry.la) or
        not re.match(r'^(\w|[#-])*$', entry.hu) or
        not entry.key.isnumeric() or
        not entry.status in {"u", "c", "p", "e"} or
        not entry.pos in {"N", "A", "G", "U", "V", "D"} or
        entry.definition == "" or
        not re.match(r'%', entry.comment)
    )

for line_num, line in old_lines.items():
    if syntax_error(line):
        print(str(line))  # python magic method __XX__

with open(new_fn, 'w') as outfile:
    for entry_id in old_lines:
        print(str(old_lines[entry_id]), file=outfile)
<commit_msg>Fixed filled values<commit_after>import csv
import sys
import re

old_fn = sys.argv[1]
new_fn = sys.argv[2]

class DicLine:
    def __init__(self, en, hu, la, pl, key, status="u", pos="N", definition="TODO", comment="%"):
        self.en = en
        self.hu = hu
        self.la = la
        self.pl = pl
        self.key = key
        self.status = status if status else "u"
        self.pos = pos if pos else "N"
        self.definition = definition if definition else "TODO"
        if comment == "" or not re.match(r'%', comment):
            self.comment = "%" + comment
        else:
            self.comment = comment

    def __str__(self):
        return '\t'.join([self.en, self.hu, self.la, self.pl, self.key, self.status, self.pos, self.definition, self.comment])

old_lines = {}
with open(old_fn) as old_file:
    csv_reader = csv.reader(old_file, delimiter='\t')
    for row in csv_reader:
        curline = DicLine(*row[:9])
        if curline.key in old_lines:
            raise ValueError(f'{curline.key} is already in use!')
        old_lines[curline.key] = curline

def syntax_error(entry):
    return (
        not re.match(r'^(\w|[#-_])*$', entry.en) or
        not re.match(r'^(\w|[#-_])*$', entry.pl) or
        not re.match(r'^(\w|[#-_])*$', entry.la) or
        not re.match(r'^(\w|[#-_])*$', entry.hu) or
        not entry.key.isnumeric() or
        not entry.status in {"u", "c", "p", "e"} or
        not entry.pos in {"N", "A", "G", "U", "V", "D"} or
        entry.definition == "" or
        not re.match(r'%', entry.comment)
    )

for line_num, line in old_lines.items():
    if syntax_error(line):
        print(str(line))  # python magic method __XX__

with open(new_fn, 'w') as outfile:
    for entry_id in old_lines:
        print(str(old_lines[entry_id]), file=outfile)
<|endoftext|>


<commit_before>#copyright ReportLab Inc. 2000
#see license.txt for license details
#history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/pdfgen/pdfimages.py?cvsroot=reportlab
#$Header: /tmp/reportlab/reportlab/pdfgen/pdfimages.py,v 1.4 2000/12/19 09:12:04 rgbecker Exp $
__version__=''' $Id: pdfimages.py,v 1.4 2000/12/19 09:12:04 rgbecker Exp $ '''
__doc__="""
Image functionality sliced out of canvas.py for generalization
"""
import os
import string
import cStringIO
from types import StringType
from reportlab.pdfbase import pdfutils
from reportlab.lib.utils import fp_str
from reportlab.lib.logger import warnOnce

try:
	import zlib
except ImportError:
	zlib = None

class PDFImage:
    def __init__(self, image, x,y, width=None, height=None):
        self.image = image
        self.point = (x,y)
        self.dimensions = (width, height)

    def jpg_imagedata(self):
        #directly process JPEG files
        #open file, needs some error handling!!
        imageFile = open(self.image, 'rb')
        info = pdfutils.readJPEGInfo(imageFile)
        imgwidth, imgheight = info[0], info[1]
        if info[2] == 1:
            colorSpace = 'DeviceGray'
        elif info[2] == 3:
            colorSpace = 'DeviceRGB'
        else: #maybe should generate an error, is this right for CMYK?
            colorSpace = 'DeviceCMYK'
        imageFile.seek(0) #reset file pointer
        imagedata = []
        #imagedata.append('BI /Width %d /Height /BitsPerComponent 8 /ColorSpace /%s /Filter [/Filter [ /ASCII85Decode /DCTDecode] ID' % (info[0], info[1], colorSpace))
        imagedata.append('BI /W %d /H %d /BPC 8 /CS /%s /F [/A85 /DCT] ID' % (imgwidth, imgheight, colorSpace))
        #write in blocks of (??) 60 characters per line to a list
        compressed = imageFile.read()
        encoded = pdfutils._AsciiBase85Encode(compressed)
        outstream = cStringIO.StringIO(encoded)
        dataline = outstream.read(60)
        while dataline <> "":
            imagedata.append(dataline)
            dataline = outstream.read(60)
        imagedata.append('EI')
        return (imagedata, imgwidth, imgheight)

    def cache_imagedata(self):
        image = self.image
        if not pdfutils.cachedImageExists(image):
            if not zlib:
                warnOnce('zlib not available')
                return
            try:
                import Image
            except ImportError:
                warnOnce('Python Imaging Library not available')
                return
            pdfutils.cacheImageFile(image)

        #now we have one cached, slurp it in
        cachedname = os.path.splitext(image)[0] + '.a85'
        imagedata = open(cachedname,'rb').readlines()
        #trim off newlines...
        imagedata = map(string.strip, imagedata)
        return imagedata

    def PIL_imagedata(self):
        if not zlib:
            warnOnce('zlib not available')
            return
        image = self.image
        myimage = image.convert('RGB')
        imgwidth, imgheight = myimage.size

        # this describes what is in the image itself
        # *NB* according to the spec you can only use the short form in inline images
        #imagedata=['BI /Width %d /Height /BitsPerComponent 8 /ColorSpace /%s /Filter [/Filter [ /ASCII85Decode /FlateDecode] ID]' % (imgwidth, imgheight,'RGB'))]
        imagedata=['BI /W %d /H %d /BPC 8 /CS /RGB /F [/A85 /Fl] ID' % (imgwidth, imgheight))]

        #use a flate filter and Ascii Base 85 to compress
        raw = myimage.tostring()
        assert(len(raw) == imgwidth * imgheight, "Wrong amount of data for image")
        compressed = zlib.compress(raw)   #this bit is very fast...
        encoded = pdfutils._AsciiBase85Encode(compressed) #...sadly this isn't

        #write in blocks of (??) 60 characters per line to a list
        outstream = cStringIO.StringIO(encoded)
        dataline = outstream.read(60)
        while dataline <> "":
            imagedata.append(dataline)
            dataline = outstream.read(60)
        imagedata.append('EI')
        return (imagedata, imgwidth, imgheight)

    def drawInlineImage(self, canvas): #, image, x,y, width=None,height=None):
        """Draw an Image into the specified rectangle.  If width and
        height are omitted, they are calculated from the image size.
        Also allow file names as well as images.  This allows a
        caching mechanism"""

        image = self.image
        (x,y) = self.point
        (width, height) = self.dimensions

        if type(image) == StringType:
            if os.path.splitext(image)[1] in ['.jpg', '.JPG', '.jpeg', '.JPEG']:
                (imagedata, imgwidth, imgheight) = self.jpg_imagedata()
            else:
                if hasattr(self,'noImageCaching') and canvas.noImageCaching:
                    imagedata = pdfutils.cacheImageFile(image,returnInMemory=1)
                else:
                    imagedata = self.cache_imagedata()
                #parse line two for width, height
                words = string.split(imagedata[1])
                imgwidth = string.atoi(words[1])
                imgheight = string.atoi(words[3])
        else:
            (imagedata, imgwidth, imgheight) = self.PIL_imagedata()
        #now build the PDF for the image.
        if not width:
            width = imgwidth
        if not height:
            height = imgheight

        # this says where and how big to draw it
        if not canvas.bottomup: y = y+height
        canvas._code.append('q %s 0 0 %s cm' % (fp_str(width), fp_str(height, x, y)))

        # self._code.extend(imagedata) if >=python-1.5.2
        for line in imagedata:
            canvas._code.append(line)

        canvas._code.append('Q')
<commit_msg>Fix typo<commit_after>#copyright ReportLab Inc. 2000
#see license.txt for license details
#history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/pdfgen/pdfimages.py?cvsroot=reportlab
#$Header: /tmp/reportlab/reportlab/pdfgen/pdfimages.py,v 1.5 2000/12/19 10:02:30 rgbecker Exp $
__version__=''' $Id: pdfimages.py,v 1.5 2000/12/19 10:02:30 rgbecker Exp $ '''
__doc__="""
Image functionality sliced out of canvas.py for generalization
"""
import os
import string
import cStringIO
from types import StringType
from reportlab.pdfbase import pdfutils
from reportlab.lib.utils import fp_str
from reportlab.lib.logger import warnOnce

try:
	import zlib
except ImportError:
	zlib = None

class PDFImage:
    def __init__(self, image, x,y, width=None, height=None):
        self.image = image
        self.point = (x,y)
        self.dimensions = (width, height)

    def jpg_imagedata(self):
        #directly process JPEG files
        #open file, needs some error handling!!
        imageFile = open(self.image, 'rb')
        info = pdfutils.readJPEGInfo(imageFile)
        imgwidth, imgheight = info[0], info[1]
        if info[2] == 1:
            colorSpace = 'DeviceGray'
        elif info[2] == 3:
            colorSpace = 'DeviceRGB'
        else: #maybe should generate an error, is this right for CMYK?
            colorSpace = 'DeviceCMYK'
        imageFile.seek(0) #reset file pointer
        imagedata = []
        #imagedata.append('BI /Width %d /Height /BitsPerComponent 8 /ColorSpace /%s /Filter [/Filter [ /ASCII85Decode /DCTDecode] ID' % (info[0], info[1], colorSpace))
        imagedata.append('BI /W %d /H %d /BPC 8 /CS /%s /F [/A85 /DCT] ID' % (imgwidth, imgheight, colorSpace))
        #write in blocks of (??) 60 characters per line to a list
        compressed = imageFile.read()
        encoded = pdfutils._AsciiBase85Encode(compressed)
        outstream = cStringIO.StringIO(encoded)
        dataline = outstream.read(60)
        while dataline <> "":
            imagedata.append(dataline)
            dataline = outstream.read(60)
        imagedata.append('EI')
        return (imagedata, imgwidth, imgheight)

    def cache_imagedata(self):
        image = self.image
        if not pdfutils.cachedImageExists(image):
            if not zlib:
                warnOnce('zlib not available')
                return
            try:
                import Image
            except ImportError:
                warnOnce('Python Imaging Library not available')
                return
            pdfutils.cacheImageFile(image)

        #now we have one cached, slurp it in
        cachedname = os.path.splitext(image)[0] + '.a85'
        imagedata = open(cachedname,'rb').readlines()
        #trim off newlines...
        imagedata = map(string.strip, imagedata)
        return imagedata

    def PIL_imagedata(self):
        if not zlib:
            warnOnce('zlib not available')
            return
        image = self.image
        myimage = image.convert('RGB')
        imgwidth, imgheight = myimage.size

        # this describes what is in the image itself
        # *NB* according to the spec you can only use the short form in inline images
        #imagedata=['BI /Width %d /Height /BitsPerComponent 8 /ColorSpace /%s /Filter [/Filter [ /ASCII85Decode /FlateDecode] ID]' % (imgwidth, imgheight,'RGB')]
        imagedata=['BI /W %d /H %d /BPC 8 /CS /RGB /F [/A85 /Fl] ID' % (imgwidth, imgheight)]

        #use a flate filter and Ascii Base 85 to compress
        raw = myimage.tostring()
        assert(len(raw) == imgwidth * imgheight, "Wrong amount of data for image")
        compressed = zlib.compress(raw)   #this bit is very fast...
        encoded = pdfutils._AsciiBase85Encode(compressed) #...sadly this isn't

        #write in blocks of (??) 60 characters per line to a list
        outstream = cStringIO.StringIO(encoded)
        dataline = outstream.read(60)
        while dataline <> "":
            imagedata.append(dataline)
            dataline = outstream.read(60)
        imagedata.append('EI')
        return (imagedata, imgwidth, imgheight)

    def drawInlineImage(self, canvas): #, image, x,y, width=None,height=None):
        """Draw an Image into the specified rectangle.  If width and
        height are omitted, they are calculated from the image size.
        Also allow file names as well as images.  This allows a
        caching mechanism"""

        image = self.image
        (x,y) = self.point
        (width, height) = self.dimensions

        if type(image) == StringType:
            if os.path.splitext(image)[1] in ['.jpg', '.JPG', '.jpeg', '.JPEG']:
                (imagedata, imgwidth, imgheight) = self.jpg_imagedata()
            else:
                if hasattr(self,'noImageCaching') and canvas.noImageCaching:
                    imagedata = pdfutils.cacheImageFile(image,returnInMemory=1)
                else:
                    imagedata = self.cache_imagedata()
                #parse line two for width, height
                words = string.split(imagedata[1])
                imgwidth = string.atoi(words[1])
                imgheight = string.atoi(words[3])
        else:
            (imagedata, imgwidth, imgheight) = self.PIL_imagedata()
        #now build the PDF for the image.
        if not width:
            width = imgwidth
        if not height:
            height = imgheight

        # this says where and how big to draw it
        if not canvas.bottomup: y = y+height
        canvas._code.append('q %s 0 0 %s cm' % (fp_str(width), fp_str(height, x, y)))

        # self._code.extend(imagedata) if >=python-1.5.2
        for line in imagedata:
            canvas._code.append(line)

        canvas._code.append('Q')
<|endoftext|>


<commit_before># -*- coding: iso-8859-1 -*-
# /usr/lib/python2.7/moacon.py
# moacon/moacon1.py: the DB-API 1.0 interface
#
# Copyright (C) 2016 Sergio Sanchez <sergio.sanchezs@ingenieros.com>
#
# This file is part of Rpi-IoT.
#
# This software is provided 'as-is', without any express or implied
# warranty.  In no event will the authors be held liable for any damages
# arising from the use of this software.
#
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# freely, subject to the following restrictions:
#
# 1. The origin of this software must not be misrepresented; you must not
#	claim that you wrote the original software. If you use this software
#	in a product, an acknowledgment in the product documentation would be
#	appreciated but is not required.
# 2. Altered source versions must be plainly marked as such, and must not be
#	misrepresented as being the original software.
# 3. This notice may not be removed or altered from any source distribution.
#
# The porpose of this software is to use the Moacon Modules from ComfileTech.com
# with the RASPBERRY PI MODELS A+, B+, RASPBERRY PI 2 B AND RASPBERRY PI 3 B,
# with the industrial board named "RPi Industrial IOT Gateway" where you can
# get in www.rpi-iot.com

import serial
import time
# import sys

def OpenSerialfor_Moacon():
	global ser_moacon
	# This function works well for Raspberry Pi 3 when you disable the bluetooth and
	# swap the virtual or software serial port with the fisical serial port.
	# this apply for several raspberry models
	port = '/dev/serial0'
	ser_moacon = serial.Serial()
	ser_moacon.port = "/dev/serial0"
	ser_moacon.baudrate = 115200
	ser_moacon.parity = serial.PARITY_NONE
	ser_moacon.stopbits = serial.STOPBITS_ONE
	ser_moacon.bytesize = serial.EIGHTBITS
	ser_moacon.timeout = 0.01
	# it can be called like this:
	# ser_moacon = serial.serial('/dev/ttyS0', 115200)
	try:
		# Open a Moacon Serial port
		ser_moacon.open()
		print "serial port opened"
		return True
	except Exception as e:
		print "The serial port named: " + port + " couldn't open, Error: " + str(e)
		return False
		ser_moacon.close()

def CloseSerialfor_Moacon():
	global ser_moacon
	ser_moacon.close()

def checking_CRC_sum(data_str):
	l = len(data_str)
	check_crc = ord(data_str[1])
	for n in range(2, l-1):		# l-1: with this the sum must be zero.
		check_crc = check_crc ^ ord(data_str[n])
	return check_crc

#//=========================================================================================================================
#//=				FUNCION QUE EXTRAE LA DATA REAL DE LAS TRAMAS RECIBIDAS POR LOS MODULOS RS-ADIN4 Y RS-SADIN6			=
#//=========================================================================================================================
def FixUp_RSModule_Data(read_data, debug=False):
	lenght_readData = len(read_data)

	if debug:
		print "Lenght: " + str(lenght_readData) + " -RAW Data: "
		for num in range(0,lenght_readData):
			print hex(ord(read_data[num])),
		print ""

	_MBrxdata = ""
	k = 0
	while k < lenght_readData:
		# swapping the "0x10 0x30" to "0x10"
		if read_data[k] == chr(0x10) and read_data[k+1] == chr(0x30):
			_MBrxdata += chr(0x10)
			k += 1	# Aditional jump to get analize the next raw data
		# swapping the "0x10 0x22" to "0x02"
		elif read_data[k] == chr(0x10) and read_data[k+1] == chr(0x22):
			_MBrxdata += chr(0x02)
			k += 1	# Aditional jump to get analize the next raw data
		# swapping the "0x10 0x23" to "0x03"
		elif read_data[k] == chr(0x10) and read_data[k+1] == chr(0x23):
			_MBrxdata += chr(0x03)
			k += 1	# Aditional jump to get analize the next raw data
		# Deleting the "0x4E 0x00", it is a data division of RS-ADIN4 Module
		elif read_data[k] == chr(0x4E) and read_data[k+1] == chr(0x00):
			k += 1	# Aditional jump to get analize the next raw data
		# Deleting the "0x44 0x00", it is a data division of RS-ADIN4 Module
		elif read_data[k] == chr(0x44) and read_data[k+1] == chr(0x00):
			k += 1	# Aditional jump to get analize the next raw data
		# If doesn't exist a coincidence with previous, we keep the raw data
		else:
			_MBrxdata += read_data[k]
		k += 1	# Normal jump to get analize the next raw data

	# Se imprimen los datos recibidos procesados con datos reales.
	if debug:
		real_lenght = len(_MBrxdata)
		print "Lenght: " + str(lenght_readData) + " -Real Data: "
		for num in range(0,real_lenght):
			print hex(ord(_MBrxdata[num])),
		print ""

	return _MBrxdata

"""
=========================================================================================================================
=					READ FUNCTION OF MOACON MODULE RS_SADIN6 - SPEED COMMUNICATION: 115.200 BAUD						=
=	Buy this module in: http://www.comfiletech.com/rs-sadin6-ad-input/													=
=	New version: V1.2	Release Date:	2017-01-27																		=
=	It proccess the data before verify the check sum CRC																=
=========================================================================================================================
"""
def RS_SADIN6(ReadModule, debug=False):
	RS_Code = 0x27	# Defining the code of RS-SADIN6 Modules
	numChannels = 6	# Number of channels in the ADC Module
	# debug = False	# If you wanna to activate debug (print) for testing
	Values = RS_Read(ReadModule, RS_Code, numChannels, debug)
	return Values
"""
=========================================================================================================================
=					READ FUNCTION OF MOACON MODULE RS_ADIN4 - SPEED COMMUNICATION: 115.200 BAUD							=
=	Buy this module in: http://www.comfiletech.com/rs-adin4-ad-input/													=
=	New version: V1.2	Release Date:	2017-01-27																		=
=	It verify the check sum CRC before it proccess the data															=
=========================================================================================================================
"""
def RS_ADIN4(ReadModule, debug=False):
	RS_Code = 0x21	# Defining the code of RS-ADIN4 Modules
	numChannels = 4	# Number of channels in the ADC Module
	# debug = False	# If you wanna to activate debug (print) for testing
	Values = RS_Read(ReadModule, RS_Code, numChannels, debug)
	return Values
"""
=========================================================================================================================
=			READ FUNCTION OF MOACON MODULES RS_ADIN4 and RS_SADIN6 - SPEED COMMUNICATION: 115.200 BAUD					=
=	New version: V1.2	Release Date:	2017-01-27																		=
=========================================================================================================================
"""
def RS_Read (ReadModule, RS_Code, numChannels, debug):
	global ser_moacon		# Getting global variable of serial port
	RS_ADIN4_Code = 0x21	# Defining the code of RS-ADIN4 Modules
	RS_SADIN6_Code = 0x27	# Defining the code of RS-SADIN6 Modules
	STX = 0x02				# Start of Text
	ETX = 0x03				# End of Text
	rd_start = 0x30			# Position of the first module from ASCII code (0x30 = '0')
	count_attemps_read = 0	# Counter of how many time it request data from modules
	max_attemps_read = 3	# Maximum attemps

	module_number = ReadModule + rd_start

	_Txb = [chr(STX), chr(RS_Code), chr(module_number), chr(ETX)]		# Defining the basic array
	crc = chr( RS_Code ^ (module_number) )							# Calculating the CRC

	if debug: print "The Real CRC is: " + hex(ord(crc))

	# Checking the CRC for hexadecimals 02, 03 and 10 in the frames.
	if crc == chr(0x02):	# the 02 is replace with 0x10 and 0x22
		_Txb.insert(3,chr(0x10))
		_Txb.insert(4,chr(0x22))
	elif crc == chr(0x03):	# the 03 is replace with 0x10 and 0x23
		_Txb.insert(3,chr(0x10))
		_Txb.insert(4,chr(0x23))
	elif crc == chr(0x10):	# the 10 is replace with 0x10 and 0x30
		_Txb.insert(3,chr(0x10))
		_Txb.insert(4,chr(0x30))
	else:	# if dont found any of those charaters just put in the frame the real CRC
		_Txb.insert(3,crc)

	# passing all the array data to a string data
	tx_data = ""
	for data in _Txb:
		# print hex(ord(data))
		tx_data += data

	while count_attemps_read < max_attemps_read:

		ser_moacon.flushInput()
		if debug: print "=============================================="
		if debug: print "Transmit: " + str(_Txb)
		ser_moacon.write(tx_data)		# Send the request channels data to moacon module
		count_attemps_read += 1

		read_data = ""
		read_data = ser_moacon.read(200)	# Read the data arrived from moacon modules
		# print type(read_data)
		# read_data = readuntilchr03(ser_moacon, tx_data)

		# Print data to view how received the data
		lenght_readData = len(read_data)
		# if debug: print "Lenght: " + str(lenght_readData) + " -RAW Data: "
		# for num in range(0,lenght_readData):
		# 	if debug:
		# 		print hex(ord(read_data[num])),
		# if debug:
		# 	print ""
		# 	print "Read size: " + str(len(read_data))

		# Checking if there is data en read_data
		if lenght_readData > 0:
			# RS_SADIN6 proccess the data before verify the check sum CRC.
			# if RS_Code == RS_SADIN6_Code:	# *¨*******************
			check_crc = checking_CRC_sum(read_data)							# Checking if the CRC of data is OK
			crc_flag = False
			if check_crc == 0:
				crc_flag = True
				if debug: print "CRC check BEFORE Fixed up the data received"

			read_data_fixedup = FixUp_RSModule_Data(read_data, debug)		# Function that fix all modbus data to real data

			if crc_flag == False:
				check_crc = checking_CRC_sum(read_data_fixedup)					# Checking if the CRC of data is OK
				if debug: print "CRC check AFTER Fixed up the data received"

			if debug: print "Check CRC: " + str(hex(check_crc))

			flag_pasted_frames = False		# Flag of joined frames. flag=True:
			# poner un if len(read_data_fixedup) > 5:
			if ord(read_data_fixedup[1]) == RS_Code and check_crc == 0 and ord(read_data_fixedup[2]) == module_number:
				# Extraer, procesar y retornar arreglo
				if debug: print "No spliting, CRC ===== OK == OK == OK ====="
				count_attemps_read = 3
				pass
			else:
				# read_data_fixedup = read_data_fixedup[0:read_data_fixedup.index(chr(0x03))+1]
				if debug: print "Looking two frames together"
				try:
					read_data_fixedup.index(chr(0x03)+chr(0x02))	# Verify if there are two joined frames searching 0x03 and 0x02 in hex
				except Exception as e:
					# Si detecta que no hay tramas unidas
					# If detects that arent joined frame
					if debug: print "No frames together"
					flag_pasted_frames = True		# Flag that that show at the end if in the data received are two joined frames
					pass
				else:	# Si hay 2 tramas unidas, Se separan en dos variables para analizar cual es la verdadera con el CRC
					if debug: print "frames together found"
					read_data1 = read_data_fixedup[0 : read_data_fixedup.index(chr(0x03)+chr(0x02)) + 1]
					read_data2 = read_data_fixedup[read_data_fixedup.index(chr(0x03)+chr(0x02)) + 1 : lenght_readData + 1]
					# Calcular CRC de la parte 1 y 2
					check_crc1 = checking_CRC_sum(read_data1)
					check_crc2 = checking_CRC_sum(read_data2)
					if debug:
						print "Real size data1: " + str(read_data1.index(chr(0x03))+1)
						print "Check CRC1: " + str(hex(check_crc1))
						print "Real size data2: " + str(read_data2.index(chr(0x03))+1)
						print "Check CRC2: " + str(hex(check_crc2))
					# Revisar si los checksum son iguales a 0 y el código coincide
					if ord(read_data1[1]) == RS_Code and check_crc1 == 0 and ord(read_data_fixedup[2]) == module_number:
						# Extraer, procesar y retornar arreglo de la parte 1
						read_data = read_data1
						count_attemps_read = max_attemps_read
						pass
					elif ord(read_data2[1]) == RS_Code and check_crc2 == 0 and ord(read_data_fixedup[2]) == module_number:
						# Extraer, procesar y retornar arreglo de la parte 2
						read_data = read_data2
						count_attemps_read = max_attemps_read
						pass
					else:	# Si no es ninguna de las dos no hace nada, la idea es que pase a revisar si hay mas tramas unidas
						flag_pasted_frames = True
						pass

			# if read_data_fixedup.index(chr(0x03)+chr(0x02)):
			# 	read_data_fixedup = read_data_fixedup[read_data_fixedup.index(chr(0x03)+chr(0x02))+1:lenght_readData+1]

			if not flag_pasted_frames:
				if debug: print "Real size: " + str(read_data_fixedup.index(chr(0x03))+1)
				RawData = []
				if len(read_data_fixedup) > 6:
					# RS_ADIN4 verify the check sum CRC before the proccess the data
					# if RS_Code == RS_ADIN4_Code: read_data_fixedup = FixUp_RSModule_Data(read_data_fixedup)	# Function that fix all modbus data to real data***************
					k = 3
					data = 0
					if debug: print "Array size: " + str(len(read_data_fixedup))
					"""Cicle to extract the real ADC data to proccess in the specific application"""
					for n in range(0, numChannels):
						data = ( ord(read_data_fixedup[k]) * 256 ) + ord(read_data_fixedup[k+1])
						if data >= 11000:
							data = -1
						RawData.insert(n, data)
						# print "Dato " + str(n+1) + ": " + str(RawData[n])		# Print example
						k += 2
					return RawData
				else:
					for n in range(0, numChannels):
						RawData.insert(n, -2)
					return RawData

			elif count_attemps_read == max_attemps_read:	# entra cuando no hay tramas juntas
				RawData = []
				for n in range(0, numChannels):
					RawData.insert(n, -3)
				return RawData

			time.sleep(.03)
		# if there is no data in read_data_fixedup fill the array with -4 to show an error.
		else:
			RawData = []
			for n in range(0, numChannels):
				RawData.insert(n, -4)
			return RawData

		"""
		When data result in each channel is:
		-> 1: probably the channel is damaged.
		-> 2: Probably the data received is incomplete.
		-> 3: It tried to read 3 times and it couldn't get an answer from the module.
		-> 4: Module is Disconnected. Please verify the connection.
		"""

"""
=====================================================================================
=			READING EXAMPLE CODE FOR RS_ADIN4. TESTING AND WORKING					=
=====================================================================================
"""
# import serial
# import time
# import IOT.moacon
# ModuleNumber = 1
# if __name__ == '__main__':
# 	if IOT.moacon.OpenSerialfor_Moacon() is True:
# 		results = IOT.moacon.RS_ADIN4(ModuleNumber)
# 		# print results
# 		print "Module " + str(ModuleNumber)
# 		for res in range(0, 4):
# 			print "Channel " + str(res+1) + ": " + str(results[res])
# 	IOT.moacon.CloseSerialfor_Moacon()
"""
=====================================================================================
=			READING EXAMPLE CODE FOR RS_SADIN6. TESTING AND WORKING					=
=====================================================================================
"""
# import serial
# import time
# import IOT.moacon
# ModuleNumber = 7
# if __name__ == '__main__':
# 	if IOT.moacon.OpenSerialfor_Moacon() is True:
# 		results = IOT.moacon.RS_SADIN6(ModuleNumber)
# 		# print results
# 		print "Module " + str(ModuleNumber)
# 		for res in range(0, 6):
# 			print "Channel " + str(res+1) + ": " + str(results[res])
# 	IOT.moacon.CloseSerialfor_Moacon()

ModuleNumber = 1
if __name__ == '__main__':

	while True:
		if OpenSerialfor_Moacon() is True:

			for ModuleNumber in range(1,4):
				results = RS_ADIN4(ModuleNumber, True)
				print "Module " + str(ModuleNumber) + ": "
				for res in range(0, 3):
					print "Channel " + str(res+1) + ": " + str(results[res])
				# time.sleep(.01)
			# Recolecting data from digital inputs
			time.sleep(1)

	CloseSerialfor_Moacon()<commit_msg>Delete debug_loop_rs-adin4.py<commit_after><|endoftext|>