github / src / client.py @ a5c338e8
1 |
# Copyright (c) 2016, LE GOFF Vincent |
---|---|
2 |
# All rights reserved.
|
3 |
|
4 |
# Redistribution and use in source and binary forms, with or without
|
5 |
# modification, are permitted provided that the following conditions are met:
|
6 |
|
7 |
# * Redistributions of source code must retain the above copyright notice, this
|
8 |
# list of conditions and the following disclaimer.
|
9 |
|
10 |
# * Redistributions in binary form must reproduce the above copyright notice,
|
11 |
# this list of conditions and the following disclaimer in the documentation
|
12 |
# and/or other materials provided with the distribution.
|
13 |
|
14 |
# * Neither the name of ytranslate nor the names of its
|
15 |
# contributors may be used to endorse or promote products derived from
|
16 |
# this software without specific prior written permission.
|
17 |
|
18 |
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19 |
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20 |
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21 |
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
22 |
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23 |
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
24 |
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
25 |
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
26 |
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27 |
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28 |
|
29 |
"""This file contains the client that can connect to a MUD.
|
30 |
|
31 |
It is launched in a new thread, so as not to block the main thread.
|
32 |
|
33 |
"""
|
34 |
|
35 |
import os |
36 |
import re |
37 |
from telnetlib import Telnet, WONT, WILL, ECHO |
38 |
import threading |
39 |
import time |
40 |
|
41 |
try:
|
42 |
from UniversalSpeech import say |
43 |
from UniversalSpeech import braille as display_braille |
44 |
except ImportError: |
45 |
say = None
|
46 |
display_braille = None
|
47 |
|
48 |
from sharp.engine import SharpScript |
49 |
|
50 |
# Constants
|
51 |
ANSI_ESCAPE = re.compile(r'\x1b[^m]*m')
|
52 |
|
53 |
class Client(threading.Thread): |
54 |
|
55 |
"""Class to receive data from the MUD."""
|
56 |
|
57 |
def __init__(self, host, port=4000, timeout=0.1, engine=None, |
58 |
world=None):
|
59 |
"""Connects to the MUD."""
|
60 |
threading.Thread.__init__(self)
|
61 |
self.client = None |
62 |
self.timeout = timeout
|
63 |
self.engine = engine
|
64 |
self.world = world
|
65 |
self.running = False |
66 |
self.sharp_engine = SharpScript(engine, self, world) |
67 |
|
68 |
# Try to connect to the specified host and port
|
69 |
self.client = Telnet(host, port)
|
70 |
self.running = True |
71 |
|
72 |
def run(self): |
73 |
"""Run the thread."""
|
74 |
while self.running: |
75 |
time.sleep(self.timeout)
|
76 |
msg = self.client.read_very_eager()
|
77 |
if msg:
|
78 |
for line in msg.splitlines(): |
79 |
for trigger in self.world.triggers: |
80 |
trigger.feed(line) |
81 |
|
82 |
self.handle_message(msg)
|
83 |
|
84 |
def handle_message(self, msg, force_TTS=False, screen=True, |
85 |
speech=True, braille=True): |
86 |
"""When the client receives a message.
|
87 |
|
88 |
Parameters
|
89 |
msg: the text to be displayed (str)
|
90 |
force_TTS: should the text be spoken regardless?
|
91 |
screen: should the text appear on screen?
|
92 |
speech: should the speech be enabled?
|
93 |
braille: should the braille be enabled?
|
94 |
|
95 |
"""
|
96 |
pass
|
97 |
|
98 |
def write(self, text): |
99 |
"""Write text to the client."""
|
100 |
if text.startswith("#"): |
101 |
self.sharp_engine.execute(text)
|
102 |
else:
|
103 |
self.client.write(text)
|
104 |
|
105 |
|
106 |
class GUIClient(Client): |
107 |
|
108 |
"""Client specifically linked to a GUI window.
|
109 |
|
110 |
This client proceeds to send the text it receives to the frame.
|
111 |
|
112 |
"""
|
113 |
|
114 |
def __init__(self, host, port=4000, timeout=0.1, engine=None, |
115 |
world=None):
|
116 |
Client.__init__(self, host, port, timeout, engine, world)
|
117 |
self.window = None |
118 |
if self.client: |
119 |
self.client.set_option_negotiation_callback(self.handle_option) |
120 |
|
121 |
def link_window(self, window): |
122 |
"""Link to a window (a GUI object).
|
123 |
|
124 |
This objectt can be of various types. The client only interacts
|
125 |
with it in two ways: First, whenever it receives a message,
|
126 |
it sends it to the window's 'handle_message' method. It also
|
127 |
calls the window's 'handle_option' method whenever it receives
|
128 |
a Telnet option that it can recognize.
|
129 |
|
130 |
"""
|
131 |
self.window = window
|
132 |
window.client = self
|
133 |
|
134 |
def handle_message(self, msg, force_TTS=False, screen=True, |
135 |
speech=True, braille=True): |
136 |
"""When the client receives a message.
|
137 |
|
138 |
Parameters
|
139 |
msg: the text to be displayed (str)
|
140 |
force_TTS: should the text be spoken regardless?
|
141 |
screen: should the text appear on screen?
|
142 |
speech: should the speech be enabled?
|
143 |
braille: should the braille be enabled?
|
144 |
|
145 |
"""
|
146 |
encoding = self.engine.settings["options.general.encoding"] |
147 |
msg = msg.decode(encoding, "replace")
|
148 |
msg = ANSI_ESCAPE.sub('', msg)
|
149 |
if self.window and screen: |
150 |
self.window.handle_message(msg)
|
151 |
|
152 |
# In any case, tries to find the TTS
|
153 |
if self.engine.TTS_on or force_TTS: |
154 |
# If outside of the window
|
155 |
window = self.window
|
156 |
focus = window.focus if window else True |
157 |
if not focus and not self.engine.settings["options.TTS.outside"]: |
158 |
if not force_TTS: |
159 |
return
|
160 |
|
161 |
if say and speech: |
162 |
say(msg, interrupt=False)
|
163 |
if braille and display_braille: |
164 |
display_braille(msg) |
165 |
|
166 |
def handle_option(self, socket, command, option): |
167 |
"""Handle a received option."""
|
168 |
name = ""
|
169 |
if command == WILL and option == ECHO: |
170 |
name = "hide"
|
171 |
elif command == WONT and option == ECHO: |
172 |
name = "show"
|
173 |
|
174 |
if name and self.window: |
175 |
self.window.handle_option(name)
|