File: chatserver.py

package info (click to toggle)
circuits 3.2.3-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,980 kB
  • sloc: python: 17,583; javascript: 3,226; makefile: 100
file content (140 lines) | stat: -rwxr-xr-x 3,574 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/usr/bin/env python
"""
Chat Server Example

This example demonstrates how to create a very simple telnet-style chat
server that supports many connecting clients.
"""

from optparse import OptionParser

from circuits import Component, Debugger
from circuits.net.events import write
from circuits.net.sockets import TCPServer


__version__ = '0.0.1'

USAGE = '%prog [options]'
VERSION = '%prog v' + __version__


def parse_options():
    parser = OptionParser(usage=USAGE, version=VERSION)

    parser.add_option(
        '-b',
        '--bind',
        action='store',
        type='string',
        default='0.0.0.0:8000',
        dest='bind',
        help='Bind to address:[port]',
    )

    parser.add_option(
        '-d',
        '--debug',
        action='store_true',
        default=False,
        dest='debug',
        help='Enable debug mode',
    )

    opts, args = parser.parse_args()

    return opts, args


class ChatServer(Component):
    def init(self, args, opts):
        """
        Initialize our ``ChatServer`` Component.

        This uses the convenience ``init`` method which is called after the
        component is properly constructed and initialized and passed the
        same args and kwargs that were passed during construction.
        """
        self.args = args
        self.opts = opts

        self.clients = {}

        if opts.debug:
            Debugger().register(self)

        if ':' in opts.bind:
            address, port = opts.bind.split(':')
            port = int(port)
        else:
            address, port = opts.bind, 8000

        bind = (address, port)

        TCPServer(bind).register(self)

    def broadcast(self, data, exclude=None):
        exclude = exclude or []
        targets = (sock for sock in self.clients if sock not in exclude)
        for target in targets:
            self.fire(write(target, data))

    def connect(self, sock, host, port):
        """Connect Event -- Triggered for new connecting clients"""
        self.clients[sock] = {
            'host': sock,
            'port': port,
            'state': {
                'nickname': None,
                'registered': False,
            },
        }

        self.fire(write(sock, b'Welcome to the circuits Chat Server!\n'))
        self.fire(write(sock, b'Please enter a desired nickname: '))

    def disconnect(self, sock):
        """Disconnect Event -- Triggered for disconnecting clients"""
        if sock not in self.clients:
            return

        nickname = self.clients[sock]['state']['nickname']

        self.broadcast(
            f'!!! {nickname:s} has left !!!\n'.encode(),
            exclude=[sock],
        )

        del self.clients[sock]

    def read(self, sock, data):
        """Read Event -- Triggered for when client connections have data"""
        data = data.strip().decode('utf-8')

        if not self.clients[sock]['state']['registered']:
            nickname = data
            self.clients[sock]['state']['registered'] = True
            self.clients[sock]['state']['nickname'] = nickname

            self.broadcast(
                f'!!! {nickname:s} has joined !!!\n'.encode(),
                exclude=[sock],
            )
        else:
            nickname = self.clients[sock]['state']['nickname']

            self.broadcast(
                f'<{nickname:s}> {data:s}\n'.encode(),
                exclude=[sock],
            )


def main():
    opts, args = parse_options()

    # Configure and "run" the System.
    ChatServer(args, opts).run()


if __name__ == '__main__':
    main()