last state we had in cvs

This commit is contained in:
psyc://psyced.org/~lynX 2010-02-24 09:50:45 +01:00
commit 0f02e9cd76
128 changed files with 9224 additions and 0 deletions

70
AGENDA.txt Normal file
View File

@ -0,0 +1,70 @@
Meaning of states:
X: done
-: started or halfway done
?: I have no clue what this is about but it sounds important
: nothing done yet but may have a clue how to do it
(note: then make a sketch)
VERSION 0.1
===========
[X] connect to a main psyc UNI via TCP
[X] make it working again automatically, write a package
[X] configfile parsing
[X] implement psyctext parser
[X] actually it is a mmptext-parser as well
[X] parse MMP and PSYCpackets
[ ] implement augment und diminish, but atm muve does not support it
[X] create state objects / machines
[X] handler architecture for psyc functions (requires state objects)
[X] interface definition (taken from bitkoenig's jaPSYC)
[X] basic layout for the classes and objects
[X] support for multiple GUI toolkits by design
[X] make gui main objects differ from application objects
[X] draw a communicative sketch of how it works
[X] debug GUI
VERSION 0.2
===========
[ ] no fullstate sending
[ ] UDP support for some things
[?] what things? listen and wait what comes...
[ ] TCP server listening on port 4404
[?] authorization checking -> psycauth
[ ] UDP server listening on port 4404
[ ] same as for tcp
[X] implement graphical room interfaces
[X] use user input
[X] implement graphical user interfaces
[-] implement graphical friendlist
[X] think about command syntax (like: "/" for direct commands, "#" for uni commands, "!" for sending rooms a request_execute
VERSION 0.3
===========
[ ] connect to friend unl's if you want to talk to them and they
are using a psyc interface (ask lynx for remote buddies)
[-] connect to distant rooms (how does it work? ask lynx)
is it even done via a server or is it the clients job?
VERSION 0.4
===========
[ ] parse all messages from lynx's psyced meaningfully
VERSION 0.5
===========
[-] find psyc protocol gagdets and errors and fix them
VERSION 0.6
===========
[ ] make a reference implementation testsuite (tests are good)
that identifies severe and not-so-important lacks in
clients and/or servers. all real psyc clients should support a
minimum set of commands
VERSION 1.0
===========
[-] "plug-in" gui support (Tkinter, pyGTK, pyQT, wxPython, ...)
Note: basically you only need to implement some classes...
Tkinter GUI may serve as an example for more powerful clients
[-] user-defined handler architecture for "scripting"
("users" have to write packages anyway)

21
CONTRIBUTE.txt Normal file
View File

@ -0,0 +1,21 @@
Should you wish to contribute to this program, you can receive a
write access to the software repository from symlynX. By checking
code in, you grant us permission to license your contribution under
both the GNU General Public License and under other licenses.
With the act of checking into the software repository, you guarantee
that you are either the author and copyright owner of the contribution
you are making, or that you have a license to use said contribution in
a way compatible with our terms of use. If you fail to do so, you
remain the sole responsible for your acting; no part of the
responsibility will be transferred to symlynX.
This means, for example, that you cannot contribute code elements
which stem from an other GPL project, as this would not comply with
the dual licensing requirement described above.
... if you don't like these terms, feel free to start your own repository ...
or maybe just talk with us about it?

280
COPYLEFT.txt Normal file
View File

@ -0,0 +1,280 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS

49
FLOW.txt Normal file
View File

@ -0,0 +1,49 @@
psyc://user@host psyc://host2/@place psyc://host/
user unl some nice place my uni-host
^ ^ ^
| | |
| (connections) |
| | |
-----------------------------------------------------------------
| |
| MMP |
| |
-----------------------------------------------------------------
| ^
packetReceived() |
(MMP, data) (packet generation)
| (state keeping)
v |
-----------------------------------------------------------------
| |
| PSYC |
| |
-----------------------------------------------------------------
| ^
packetReceived() |
(MMP, PSYC) |
| |
v |
-----------------------------------------------------------------
| |
| PSYCMessagecenter |
| |
-----------------------------------------------------------------
^ | ^ |
| received() | received()
register() | register() |
| v | v
--------------------------------- ------------------------------
| | | |
| PSYCPackage Interface | | GUI |
| | | implementing PSYCPackage |
--------------------------------- ------------------------------
|
update()
|
v
---------------------------------
| |
| GUI |
| |
---------------------------------

29
GUI/Abstract/Gui.py Normal file
View File

@ -0,0 +1,29 @@
"""Abstract Gui Module - serves as interface reference"""
import sys
class UserGui:
def __init__(self):
if self.__class__ == UserGui:
raise "class UserGui is abstract!"
def set_model(self, model): self.model = model
class RoomGui:
def __init___(self):
if self.__class__ == RoomGui:
raise "class RoomGui is abstract!"
def set_model(self, model): self.model = model
class MainGui:
def __init__(self, argv):
if self.__class__ == MainGui:
raise "class MainGui is abstract!"
self.center = None
def run(self):
pass
def quit(self):
self.center.quit()
sys.exit(0)
def connect(self):
pass

BIN
GUI/Abstract/Gui.pyc Normal file

Binary file not shown.

1
GUI/Abstract/__init__.py Normal file
View File

@ -0,0 +1 @@
# different user interfaces for pyPSYC

BIN
GUI/Abstract/__init__.pyc Normal file

Binary file not shown.

448
GUI/Tkinter/Gui.py Normal file
View File

@ -0,0 +1,448 @@
import GUI.Abstract.Gui as AbstractGui
import Tkinter, asyncore, sys
from pypsyc.PSYC.PSYCRoom import PSYCPackage
class FriendList(Tkinter.Listbox):
def __init__(self):
self.packagename = "Friends gui"
root = Tkinter.Toplevel(height=480, width=150)
root.title("friends online:")
self.mapping = {}
self.model = None
Tkinter.Listbox.__init__(self, root)
self.grid(row = 0, column = 0, sticky=Tkinter.E + Tkinter.W + Tkinter.N + Tkinter.S)
self.bind("<Double-Button-1>", self.on_leftclick)
self.bind("<Double-Button-3>", self.on_rightclick)
bframe = Tkinter.Frame(root)
bframe.grid(row=1, column = 0, sticky = Tkinter.W + Tkinter.E)
self.linklabel = Tkinter.Label(bframe, text="unlinked")
self.linklabel.grid(row=0, column = 0, sticky = Tkinter.W)
#u = Tkinter.Button(bframe, text="update", command=self.update_friends)
#u.grid(row=0, column = 1, sticky=Tkinter.E)
def set_model(self, model): self.model = model
def set_status(self, status):
if status == "_notice_link":
self.linklabel["text"] = "link"
elif status == "_notice_unlink":
self.linklabel["text"] = "unlink"
def on_leftclick(self, event):
if self.selection_get():
self.model.set_target(self.selection_get())
self.model.set_mc("_internal_message_private_window_popup")
self.model.castmsg()
def on_rightclick(self, event):
if self.selection_get():
#print "we should pop up a context menu for", self.selection_get()
self.model.set_target(self.selection_get())
self.model.set_mc("_request_examine")
self.model.send()
def received(self, source, mc, mmp, psyc):
# das print-zeugs is doch nicht ganz so nutzlos!
# muss es aber werden, oder?
if mc == "_notice_friend_present":
self.present(mmp.get_source())
elif mc == "_notice_friend_absent":
self.absent(mmp.get_source())
elif mc == "_print_notice_friend_present":
#print "friend present", psyc._query("_friends")
pass
elif mc == "_print_notice_friend_absent":
#print "friend absent", psyc._query("_friends")
pass
elif mc == "_print_list_friends_present":
#print "friends present", psyc._query("_friends")
#print psyc.get_text()
for friend in psyc._query("_friends").split(" "):
print friend
print "friends:", psyc.get_text()
elif mc == "_notice_link" or mc == "_notice_unlink":
self.set_status(mc)
def present(self, nick):
if not self.mapping.has_key(nick):
self.mapping[nick] = self.size() # where we insert it
self.insert(self.size(), nick)
def absent(self, nick):
if self.mapping.has_key(nick):
self.delete(self.mapping[nick])
from pypsyc.PSYC import parsetext, get_user
class UserGui(AbstractGui.UserGui):
def __init__(self):
AbstractGui.UserGui.__init__(self)
self.model = None
self.windows = {}
def makewindow(self, source):
# better check it again...
if self.windows.has_key(source):
return
win = Tkinter.Toplevel()
win.title("Dialog with " + source)
win.protocol("WM_DELETE_WINDOW", lambda: self.deletewindow(source))
dframe = Tkinter.Frame(win)
displayfield = Tkinter.Text(dframe, width=50, height=15,
state=Tkinter.DISABLED,
wrap=Tkinter.WORD)
displayfield.grid(row=0, column=0)
scrollbar = Tkinter.Scrollbar(dframe,
command=displayfield.yview)
scrollbar.grid(row=0, column=1, sticky = Tkinter.N + Tkinter.S)
displayfield.config(yscrollcommand=scrollbar.set)
dframe.pack()
entryfield = Tkinter.Text(win, width=54, height=5)
entryfield.pack()
frame = Tkinter.Frame(win)
entrybutton = Tkinter.Button(frame, text="send",
command=lambda: self.say(source))
entrybutton.grid(row = 0, column = 0)
frame.pack()
self.windows[source] = {"window" : win,
"displayfield" : displayfield,
"entryfield" : entryfield,
"button" : entrybutton}
def deletewindow(self, source):
self.windows[source]["window"].destroy()
del self.windows[source]
def append_text(self, displayfield, text, encoding="iso-8859-1"):
displayfield["state"] = Tkinter.NORMAL
displayfield.insert(Tkinter.END,
text.strip().decode(encoding) + '\n')
displayfield["state"] = Tkinter.DISABLED
displayfield.yview(Tkinter.END)
def get_input(self, entryfield, encoding="iso-8859-1"):
text = entryfield.get(0.0, Tkinter.END).encode(encoding)
entryfield.delete(0.0, Tkinter.END)
return text
def say(self, source):
self.model.set_target(source)
self.model.set_mc("_message_private")
self.model.psyc._assign("_nick", get_user(self.model.center.ME()))
text = self.get_input(self.windows[source]["entryfield"])
self.model.set_text(text.strip())
self.append_text(self.windows[source]["displayfield"], "Du sagst: " + text)
self.model.send()
def received(self, source, mc, mmp, psyc):
if mc == "_message_private":
source = mmp.get_source()
if not self.windows.has_key(source):
self.makewindow(source)
self.append_text(self.windows[source]["displayfield"],
get_user(source)+":"+parsetext(mmp, psyc))
elif mc == "_internal_message_private_window_popup":
target = mmp._query("_target")
print "target:", target
if not self.windows.has_key(target):
self.makewindow(target)
from pypsyc.PSYC import parsetext
class RoomGui(AbstractGui.RoomGui, Tkinter.Toplevel):
def __init__(self):
Tkinter.Toplevel.__init__(self)
self.model = None
self.buffers = {}
self.topics = {}
## self.menu = Tkinter.Menu(self)
## self.config(menu=self.menu)
## options = Tkinter.Menu(self.menu)
## options.add_command(label="show nicklist",
## command=self.show_nicklist)
## options.add_command(label="hide nicklist",
## command=self.hide_nicklist)
## self.menu.add_cascade(label ="Options", menu=options)
self.topiclabel = Tkinter.Label(self, text="dummy topic")
mainframe = Tkinter.Frame(self)
self.textfield = Tkinter.Text(mainframe,
wrap=Tkinter.WORD)
self.textfield.config(state=Tkinter.DISABLED)
self.textfield.grid(row=0, column=0,
sticky=Tkinter.W + Tkinter.N + Tkinter.S)
scrollbar = Tkinter.Scrollbar(mainframe,
command = self.textfield.yview)
scrollbar.grid(row=0, column=1, sticky = Tkinter.N + Tkinter.S)
self.textfield.config(yscrollcommand=scrollbar.set)
self.nicklist = Tkinter.Listbox(mainframe)
entryframe = Tkinter.Frame(self)
self.entryfield = Tkinter.Entry(entryframe)
self.entryfield.grid(sticky = Tkinter.E + Tkinter.W)
self.bleiste = Tkinter.Frame(self)
self.placebuttons = {}
l = Tkinter.Label(self.bleiste,
text="|")
l.grid(row = 0, column = 0, sticky = Tkinter.W)
self.topiclabel.grid(row=0, sticky = Tkinter.W)
mainframe.grid(row=1, sticky = Tkinter.W)
entryframe.grid(row=2, sticky = Tkinter.W)
self.bleiste.grid(row=3, sticky = Tkinter.W)
self.bind("<Return>", self.say)
def append_text(self, text, encoding="iso-8859-1"):
self.textfield["state"] = Tkinter.NORMAL
self.textfield.insert(Tkinter.END,
text.decode(encoding) + '\n')
self.textfield["state"] = Tkinter.DISABLED
self.textfield.yview(Tkinter.END)
def received(self, source, mc, mmp, psyc):
## evil
try:
context = mmp._query("_context").lower()
## print context
if mc.startswith("_notice_place_enter"):
## if _source == ME eigentlich...
if not self.placebuttons.has_key(context):
self.add_room(context)
if not self.buffers.has_key(context):
self.buffers[context] = ""
self.buffers[context] += parsetext(mmp, psyc) + '\n'
if self.model.get_context() == context:
self.append_text(parsetext(mmp, psyc))
elif mc.startswith("_notice_place_leave"):
self.buffers[context] += parsetext(mmp, psyc) + '\n'
if self.model.get_context() == context:
self.append_text(parsetext(mmp, psyc))
elif mc == '_message_public':
text = psyc._query("_nick")
if psyc._query("_action") != "":
text += " " + psyc._query("_action")
text += ": " + parsetext(mmp, psyc)
if self.model.get_context() == context:
self.append_text(text)
self.buffers[context] += text + '\n'
elif mc == "_status_place_topic":
self.topics[mmp.get_source().lower()] = parsetext(mmp, psyc)
## evil
except KeyError:
print "KeyError:", context
def get_input(self, encoding="iso-8859-1"):
text = self.entryfield.get().encode(encoding)
self.entryfield.delete(0, Tkinter.END)
return text
def say(self, event):
text = self.get_input()
if text and text[0] == '/':
# we have a command
# if we know the command, we set the appropiate mc
# else we do _request_execute
if text.startswith("/join") and text.__len__() > 16:
# 16 == len(/join psyc://h/@r)
self.model.set_mc("_request_enter")
self.model.set_target(text.split(" ")[1])
self.model.send()
elif text.startswith("/part"):
# wie waers mit /part_logout, part_home, part_type, ...
self.model.set_mc("_request_leave")
self.model.set_target(self.model.get_context())
self.model.send()
elif text.startswith("/quit"):
self.model.set_mc("_request_execute")
self.model.set_target(self.model.center.ME())
self.model.set_text("/quit")
self.model.send()
elif text.startswith("/connect"):
foo = len(text.split(" "))
if foo == 2:
self.model.center.connect(text.split(" ")[1])
elif foo == 3:
self.model.center.connect(text.split(" ")[1], text.split(" ")[2])
else:
self.model.set_target(self.model.center.ME())
self.model.set_mc("_request_execute")
self.model.set_text(text)
self.model.send()
elif text and text[0] == "#":
self.model.set_target(self.model.center.ME())
self.model.set_mc("_request_execute")
self.model.set_text("/" + text[1:])
self.model.send()
elif text and text[0] == "!":
self.model.set_target(self.model.get_context())
self.model.set_mc("_request_execute")
self.model.set_text(text)
self.model.send()
elif text:
## print "msg to", self.model.get_context()
self.model.set_target(self.model.get_context())
self.model.set_mc("_message_public")
self.model.set_text(text)
self.model.send()
def get_topic(self, context = None):
if not context: context = self.model.get_context()
if self.topics.has_key(context):
return self.topics[context]
else: return ""
def change_room(self, room):
# gucken ob wir nicklist hatten, u.u. loeschen
# dieses ding sollte eigentlich ne eigene Klasse haben...
a = self.model.get_context()
if a and self.placebuttons.has_key(a):
self.placebuttons[a]["relief"] = Tkinter.RAISED
self.placebuttons[room]["relief"] = Tkinter.SUNKEN
self.model.set_context(room)
self.title("on " + room)
short = room[room.rfind("@"):] # kurzform fuer raum... nicht ideal?
self.topiclabel["text"] = short + ":" + self.get_topic()
self.textfield.config(state=Tkinter.NORMAL)
self.textfield.delete(1.0, Tkinter.END)
self.textfield.insert(Tkinter.END, self.buffers[room])
self.textfield.config(state=Tkinter.DISABLED)
self.textfield.yview(Tkinter.END)
def add_room(self, room):
short = room[room.rfind("@"):]
button = Tkinter.Button(self.bleiste, text=short,
command=lambda: self.change_room(room))
self.placebuttons[room] = button
button.grid(row=0, column = len(self.placebuttons) - 1,
sticky = Tkinter.W)
def delete_room(self, roomname):
# delete the button?
pass
## def hide_nicklist(self):
## self.nicklist.grid_forget()
## def show_nicklist(self):
## self.nicklist.grid(row=0, column=2,
## sticky = Tkinter.E + Tkinter.N + Tkinter.S)
from pypsyc.MMP.MMPState import MMPState
from pypsyc.PSYC.PSYCState import PSYCState
from pypsyc.MMP import _isModifier
class MainWindow(Tkinter.Tk):
def __init__(self, center = None):
Tkinter.Tk.__init__(self)
self.center = center
self.title("pyPSYCgui - simple psyc client - see http://www.psyc.eu")
self.menu = Tkinter.Menu(self)
self.config(menu=self.menu)
filemenu = Tkinter.Menu(self.menu)
filemenu.add_command(label="Quit", command=self.quit)
self.menu.add_cascade(label ="File", menu=filemenu)
connectionmenu = Tkinter.Menu(self.menu)
connectionmenu.add_command(label="connect", command=self.connect)
self.menu.add_cascade(label="Connections", menu=connectionmenu)
frame = Tkinter.Frame(self)
self.displayField = Tkinter.Text(frame, height=20, width=60, state=Tkinter.DISABLED)
self.displayField.grid(row=0, column=0)
scrollbar = Tkinter.Scrollbar
scrollbar = Tkinter.Scrollbar(frame,
command = self.displayField.yview)
scrollbar.grid(row=0, column=1, sticky = Tkinter.N + Tkinter.S)
self.displayField.config(yscrollcommand=scrollbar.set)
frame.pack()
# funzt eh net
## self.scrollbar = Tkinter.Scrollbar(self)
## self.scrollbar.pack(side=Tkinter.RIGHT, fill=Tkinter.Y)
## self.scrollbar.config(command=self.displayField.yview)
self.inputField = Tkinter.Text(self, height=5, width=60)
self.inputField.pack()
Tkinter.Button(self, text="send", command=self.input).pack(
side=Tkinter.LEFT)
def connect(self, host = ''):
self.center.connect(host)
def write(self, string):
self.displayField.config(state=Tkinter.NORMAL)
self.displayField.insert(Tkinter.END, string)
self.displayField.config(state = Tkinter.DISABLED)
def input(self):
## print "TkinterGui::MainWindow::input"
cmd = self.inputField.get(1.0, Tkinter.END)
self.inputField.delete(1.0, Tkinter.END)
state = 'mmp'
mmp = MMPState()
psyc = PSYCState()
for line in cmd.split('\n'):
if line == ".":
#end of packet
break
if line == "" or (not _isModifier(line)
and state == 'mmp'):
state = 'psyc'
if state == 'mmp':
mmp.set_state(line)
continue
if state == 'psyc':
if _isModifier(line):
psyc.set_state(line)
elif line.__len__() and line[0] == '_':
psyc.set_mc(line)
else:
psyc.append_text(line)
self.center.send(mmp, psyc)
class Application(AbstractGui.MainGui):
def __init__(self, argv, center):
AbstractGui.MainGui.__init__(self, argv)
self.mainWindow = MainWindow(center)
## nah... das is eigentlich auch evil ;)
sys.stdout = self.mainWindow
def socket_check(self):
asyncore.poll(timeout=0.0)
# das hier is auch noch doof...
self.mainWindow.after(100, self.socket_check)
def run(self):
self.socket_check()
Tkinter.mainloop()

1
GUI/Tkinter/__init__.py Normal file
View File

@ -0,0 +1 @@
# different user interfaces for pyPSYC

1
GUI/__init__.py Normal file
View File

@ -0,0 +1 @@
# different user interfaces for pyPSYC

BIN
GUI/__init__.pyc Normal file

Binary file not shown.

332
GUI/wx/Gui.py Normal file
View File

@ -0,0 +1,332 @@
# ask fippo if we need all this
#import pypsyc.GUI.Abstract.Gui as AbstractGui
import sys
#from pypsyc.PSYC.PSYCRoom import PSYCPackage
# we need this
from wxPython.wx import *
from wxPython.lib.rcsizer import RowColSizer
from wxPython.lib.grids import wxFlexGridSizer
#from wxPython.lib.mixins.listctrl import wxColumnSorterMixin, wxListCtrlAutoWidthMixin
# have to read about Create_Id() so we don't have to bother with the numbers
ID_ABOUT = 1002
ID_CONNECT = 1001
ID_DISCONNECT = 10011
ID_STATUS = 9901
ID_MENU = 9902
ID_BUDDY_LIST = 9903
ID_BUDDY_LIST_DK = 990301
ID_EXIT = 1099
ID_CLOSECHATNOTEBOOK = 2099
ID_CLOSECHATNOTE = 2098
class Friend:
""" a user object """
def __init__(self, uni, unl='255.255.255.255'):
self.uni = str(uni)
self.unl = str(unl)
self.chatting = 0
self.tab = None
def chat(self, inst):
# das hier sollte zu dem schon bestehenden fenster extrawin(was direkt beim
# starten des cients erzeugt wird und beim connecten sichtbar wird
# ein tab hinzufügen
if self.chatting == 1:
inst.notebook.Show(True)
# focus the window where the chat is taking place
# still a bit buggy
# if you close a tab self.tab is wrong
inst.notebook.SetSelection(self.tab)
else:
panel = wxPanel(inst.notebook, -1)
button = wxButton(panel, ID_CLOSECHATNOTE, "close chat")
button2 = wxButton(panel, ID_STATUS, "online")
nick_box = wxTextCtrl(panel, -1, style=wxTE_READONLY)
box = wxBoxSizer(wxHORIZONTAL)
box.Add(nick_box, 1,wxEXPAND)
box.Add(button2,0,wxEXPAND)
box.Add(button,0,wxEXPAND)
text_box = wxTextCtrl(panel, -1, style=wxTE_MULTILINE|wxTE_READONLY)
entry_box = wxTextCtrl(panel, -1, style=wxTE_MULTILINE, size=wxDefaultSize)
sizer = RowColSizer()
#sizer.Add(button, pos=(1,1))
#sizer.Add(nick_box, pos=(1,2), flag=wxEXPAND)
#sizer.Add(button2, pos=(1,3))
sizer.Add(box, pos=(1,1), colspan=3,flag=wxEXPAND)
sizer.Add(text_box, pos=(2,1),flag=wxEXPAND, colspan=3)
sizer.Add(entry_box, pos=(3,1),flag=wxEXPAND, colspan=2)
sizer.AddGrowableCol(1)
#sizer.AddGrowableCol(2)
sizer.AddGrowableRow(2)
panel.SetSizer(sizer)
inst.notebook.AddPage(panel, self.uni, select=1)
self.tab = inst.notebook.GetSelection()
self.chatting = 1
def getStatus(self):
return 'On'
def stop_chat(self):
self.chatting = 0
self.tab = None
# do whatever has to be done
class TabBook(wxFrame):
""" do the actual displaying """
def __init__(self, parent, ID, title, pos=wxDefaultPosition, size=wxDefaultSize, style=wxDEFAULT_FRAME_STYLE):
wxFrame.__init__(self, parent, ID, title, pos, size, style)
menu = wxMenu()
menu.Append(ID_DISCONNECT, "&Disconnect", "bye-bye")
menu.Append(ID_ABOUT, "&About", "tritratrullala")
menu.AppendSeparator()
menu.Append(ID_EXIT, "&Exit", "leave us")
menuBar = wxMenuBar()
menuBar.Append(menu, "&File");
self.SetMenuBar(menuBar)
self.notebook = wxNotebook(self, -1)
status_panel = wxPanel(self.notebook, -1)
button = wxButton(status_panel, ID_CLOSECHATNOTEBOOK, "close status win")
self.notebook.AddPage(status_panel, 'status')
# event handling
EVT_BUTTON(self, ID_CLOSECHATNOTEBOOK, self.OnCloseMe)
#EVT_BUTTON(self, ID_CLOSECHATNOTE, self.OnCloseChat)
EVT_CLOSE(self, self.OnCloseWindow)
def newChat(self, who):
#todo: check if there is already a 'chat' with that who, make a nice panel, nicklist option?
panel = wxPanel(self.notebook, -1)
button = wxButton(panel, ID_CLOSECHATNOTE, "close chat")
self.notebook.AddPage(panel, who, select =1)
# event methods
def OnCloseChat(self, event):
where = self.notebook.GetSelection()
t = self.notebook.GetPageText(where)
self.notebook.DeletePage(where)
self.notebook.Show(True)
event.Skip()
return t
def OnCloseMe(self, event):
self.Show(False)
#self.Close(true)
event.Skip()
def OnCloseWindow(self, event):
self.Show(False)
#self.Destroy()
event.Skip()
class UserGui(wxFrame):
""" handle the "querys" / chats with a single user """
def __init__(self, parent, ID, title, pos=wxDefaultPosition, size=wxDefaultSize, style=wxDEFAULT_FRAME_STYLE):
wxFrame.__init__(self, parent, ID, title, pos, size, style)
menu = wxMenu()
menu.Append(ID_DISCONNECT, "&Disconnect", "bye-bye")
menu.Append(ID_ABOUT, "&About", "tritratrullala")
menu.AppendSeparator()
menu.Append(ID_EXIT, "&Exit", "leave us")
menuBar = wxMenuBar()
menuBar.Append(menu, "&File");
self.SetMenuBar(menuBar)
self.notebook = wxNotebook(self, -1)
status_panel = wxPanel(self.notebook, -1)
button = wxButton(status_panel, ID_CLOSECHATNOTEBOOK, "close status win")
self.notebook.AddPage(status_panel, 'status')
# event handling
EVT_BUTTON(self, ID_CLOSECHATNOTEBOOK, self.OnCloseMe)
#EVT_BUTTON(self, ID_CLOSECHATNOTE, self.OnCloseChat)
EVT_CLOSE(self, self.OnCloseWindow)
def newChat(self, who):
#todo: check if there is already a 'chat' with that who, make a nice panel, nicklist option?
panel = wxPanel(self.notebook, -1)
button = wxButton(panel, ID_CLOSECHATNOTE, "close chat")
self.notebook.AddPage(panel, who, select =1)
# event methods
def OnCloseChat(self, event):
where = self.notebook.GetSelection()
t = self.notebook.GetPageText(where)
self.notebook.DeletePage(where)
self.notebook.Show(True)
event.Skip()
return t
def OnCloseMe(self, event):
self.Show(False)
#self.Close(true)
event.Skip()
def OnCloseWindow(self, event):
self.Show(False)
#self.Destroy()
event.Skip()
class FriendList(wxFrame):
""" buddy list and other stuff """
def __init__(self, parent, ID, title, pos=wxDefaultPosition, size=wxSize(190, 400), style=wxDEFAULT_FRAME_STYLE):
wxFrame.__init__(self, parent, ID, title, pos , size, style)
# menubar, statusbar et al
self.CreateStatusBar()
self.SetStatusText("welcome to pyPSYC")
menu = wxMenu()
menu.Append(ID_CONNECT, "&Connect", "connect to the net")
menu.Append(ID_ABOUT, "&About", "tritratrullala")
menu.AppendSeparator()
menu.Append(ID_EXIT, "&Exit", "leave us")
menuBar = wxMenuBar()
menuBar.Append(menu, "&File");
self.SetMenuBar(menuBar)
##'buddy' list, perhaps ;]]
self.SampleList= []
self.buddylist_dict ={}
#self.BuddyList = wxListBox(self , ID_BUDDY_LIST,style=wxLB_NEEDED_SB|wxLB_SINGLE, choices=self.SampleList)
self.BuddyList = wxListCtrl(self, ID_BUDDY_LIST, style=wxLC_REPORT|wxLC_SINGLE_SEL|wxSUNKEN_BORDER)
self.BuddyList.InsertColumn(0, "ST")
self.BuddyList.InsertColumn(1, "Nick")# , wxLIST_FORMAT_RIGHT)
self.BuddyList.SetColumnWidth(0, 20)
##end buddy list
# define the buttons and so on at the bottom of the window
self.status = wxComboBox(self, ID_STATUS, "", choices=["", "This", "is a", "Place", "to put commands"], size=(150,-1), style=wxCB_DROPDOWN)
self.menu_button = wxButton( self, ID_MENU, 'pyPSYC')
self.exit_button = wxButton( self, ID_EXIT, 'exit')
self.con_menu = wxBoxSizer(wxHORIZONTAL)
self.con_menu.Add(self.menu_button, 1, wxALIGN_BOTTOM)
self.con_menu.Add(self.exit_button, 1, wxALIGN_BOTTOM)
sizer = wxFlexGridSizer(3, 0 , 0,0)
sizer.Add(self.BuddyList, 1, wxGROW)
sizer.Add(self.con_menu, 1,wxGROW)
sizer.Add(self.status, 1,wxGROW)
sizer.AddGrowableRow(0)
sizer.AddGrowableCol(0)
# do somethign so that the buttons don't vanish in a too small window
# this is h4x0r-style but does the job at the moment
sizer.SetItemMinSize(self.BuddyList, 30, 10)
sizer.SetMinSize(wxSize(200,280))
self.SetSizer(sizer)
self.SetAutoLayout(true)
##dunno where to put it at the moment,but believe it shouldn't be here
# wir machen uns ne instanz von dem fenster wo alle chats rein kommen sollen
self.extrawin = TabBook(self, -1, "blah blubb", size=(800, 400), style = wxDEFAULT_FRAME_STYLE)
# für buddy auswahl
self.currentBuddy = 0
# event handling
EVT_BUTTON( self, ID_EXIT, self.onExit )
EVT_BUTTON(self, ID_CONNECT, self.doConnect)
EVT_BUTTON(self, ID_CLOSECHATNOTE, self.closeChat)
EVT_MENU( self, ID_EXIT, self.onExit)
EVT_MENU(self, ID_CONNECT, self.doConnect)
##EVT_LEFT_DCLICK(self.BuddyList, self.onFriend)
EVT_LIST_ITEM_SELECTED(self, ID_BUDDY_LIST, self.OnBuddySelected)
# ect_activated muß weg sosnt wird zweimal nen chat geöffnet ;]]
EVT_LIST_ITEM_ACTIVATED(self, ID_BUDDY_LIST, self.onFriend)
def PopulateBuddyList(self):
items = self.buddylist_dict.items()
for x in range(len(items)):
nick, obj = items[x]
self.BuddyList.InsertStringItem(x ,obj.getStatus())
self.BuddyList.SetStringItem(x, 1,str(nick))
self.BuddyList.SetColumnWidth(0, wxLIST_AUTOSIZE)
self.BuddyList.SetColumnWidth(1, wxLIST_AUTOSIZE)
def getColumnText(self, index, col):
item = self.BuddyList.GetItem(index, col)
return item.GetText()
def onExit(self, event):
"""do magic stuff before closing"""
#disconnect() oder sowas
self.Close(TRUE)
def doConnect(self , event):
#todo: all the socket stuff, way to much
"""do even more fippo(psyc-proto) magic"""
# this list is created by some network magic
self.current_buddys = ['tim', 'tom', 'foo', 'neo', 'fippo', 'garrit', 'marder', 'troete', 'bar', '23', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen']
for user in self.current_buddys:
tt = Friend(user)
self.buddylist_dict[user] = tt
#print self.buddylist_dict
#print '----\n'
##self.extrawin = TabBook(self, -1, "blah blubb", size=(800, 400), style = wxDEFAULT_FRAME_STYLE)
##self.otherWin = self.extrawin
self.PopulateBuddyList()
event.Skip()
def OnBuddySelected(self, event):
self.currentBuddy = event.m_itemIndex
#print str(self.currentBuddy) +'--'
def onFriend(self, event):
#todo: catch the case that there is no extrawin(we aren't connected)
"""open a new tab in the usergui"""
tt = str(self.getColumnText(self.currentBuddy, 1))
t = self.buddylist_dict[tt]
t.chat(self.extrawin)
self.extrawin.Show(True)
#print self.getColumnText(self.currentBuddy, 1)
event.Skip()
def closeChat(self, event):
t = self.extrawin.OnCloseChat(event)
buddy = self.buddylist_dict[t]
buddy.stop_chat()
event.Skip()
class Application(wxApp):
def OnInit(self):
frame = FriendList(NULL, -1, "pyPSYC 0.0.0.0.0.1")
frame.Show(true)
self.SetTopWindow(frame)
#self.timer = wxTimer()
#self.timer.SetOwner(self.socket_check, 6666)
#self.timer.Start(5000) # alle 100 ms / 5 secs
#self.timer = wxPyTimer(self.socket_check)
#self.timer.Start(500) # alle 500 ms
return true
def run(self):
# blah mainloop
from twisted.internet import wxsupport, reactor
wxsupport.install(self)
print "running reactor..."
reactor.run()
## self.MainLoop()
## this has to change i guess
#app = PsycApp(0)
#app.MainLoop()
##EOF

1
GUI/wx/__init__.py Normal file
View File

@ -0,0 +1 @@
# different user interfaces for pyPSYC

BIN
GUI/wx/__init__.pyc Normal file

Binary file not shown.

696
GUI/wx/devGui.py Normal file
View File

@ -0,0 +1,696 @@
# be warned this is the _dev_ version of the wx gui
# perhaps it will work, perhaps it's all borked up,
# this is a plazground shouldn't be used on a day to
# day basis
import GUI.Abstract.Gui as AbstractGui
import asyncore, sys, os
import ConfigParser
from string import split, rfind, strip
from pypsyc.PSYC.PSYCRoom import PSYCPackage
from pypsyc.PSYC.PSYCRoom import Friends as FriendsPackage
from pypsyc.PSYC import parsetext, get_user
from wxPython.wx import *
from wxPython.stc import *
from wxPython.lib.rcsizer import RowColSizer
from wxPython.lib.grids import wxFlexGridSizer
VERSION = 'pyPSYC-wx 0.0.1'
# for linux/posix this should work
CONFIG_FILE = os.getenv("HOME") + "/.pypsyc/wx-config"
# windows users should uncomment the next line and comment out the line above
# CONFIG_FILE = 'wx-config'
ID_ABOUT = 1002
ID_CONNECT = 1001
ID_DISCONNECT = 10011
ID_SAY = 3300
ID_NOTEBOOK = 3301
ID_STATUS = 9901
ID_MENU = 9902
ID_BUDDY_LIST = 9903
ID_BUDDY_LIST_DK = 990301
ID_EXIT = 1099
ID_CLOSECHATNOTEBOOK = 2099
ID_CLOSECHATNOTE = 2098
def opj(path):
"""Convert paths to the platform-specific separator"""
return apply(os.path.join, tuple(path.split('/')))
class Tab(wxPanel):
def __init__(self, context, notebook):
self.notebook = notebook
wxPanel.__init__(self, self.notebook, -1)
#panel = wxPanel(self.notebook, -1)
button = wxButton(self, ID_CLOSECHATNOTE, "close chat")
#button2 = wxButton(self, ID_STATUS, "NOOP")
self.nick_box = wxTextCtrl(self, -1, style=wxTE_READONLY)
box = wxBoxSizer(wxHORIZONTAL)
box.Add(self.nick_box, 1,wxEXPAND)
#box.Add(button2,0,wxEXPAND)
box.Add(button,0,wxEXPAND)
#scroller = wxScrolledWindow(self, -1, size=(-1,-1))
self.text_box = wxTextCtrl(self, -1, style=wxTE_MULTILINE|wxTE_RICH2|wxTE_READONLY|wxTE_AUTO_URL, size=wxDefaultSize)
self.entry_box = wxTextCtrl(self, ID_SAY, style=wxTE_PROCESS_ENTER|wxTE_PROCESS_TAB, size=wxDefaultSize)
sizer = RowColSizer()
sizer.Add(box, pos=(1,1), colspan=3,flag=wxEXPAND)
sizer.Add(self.text_box, pos=(2,1),flag=wxEXPAND, colspan=3)
#sizer.Add(scroller, pos=(2,1),flag=wxEXPAND, colspan=3)
sizer.Add(self.entry_box, pos=(3,1),flag=wxEXPAND, colspan=2)
sizer.AddGrowableCol(1)
sizer.AddGrowableRow(2)
self.SetSizer(sizer)
def append_text(self, text):
#text = text.decode('iso-8859-1')
if os.name == 'posix':
#self.text_box.SetEditable(True)
#ende = self.text_box.GetInsertionPoint()
#self.text_box.ShowPosition(ende+len(text))
self.text_box.AppendText(text)
#self.text_box.SetInsertionPointEnd()
#ende = self.text_box.GetInsertionPoint()
#self.text_box.ShowPosition(ende+len(text))
self.text_box.ScrollLines(2)
#self.text_box.SetEditable(False)
else:
self.text_box.WriteText(text)
self.text_box.ScrollLines(2)
def clear_entry(self):
self.entry_box.Clear()
self.entry_box.SetInsertionPoint(0)
def append_entry(self, text):
self.entry_box.AppendText(text)
def set_topic(self, text):
self.nick_box.Clear()
self.nick_box.AppendText(text)
def get_text(self):
return self.entry_box.GetValue()
class FriendList(wxFrame, PSYCPackage):
def __init__(self, parent=None, ID=0, title='pyPSYC', center=None, config=None, pos=wxDefaultPosition, size=wxSize(190, 400), style=wxDEFAULT_FRAME_STYLE):
# bei mir is friendlist, firendlist und maingui in einem
# zumindestens sollte es das sein ob es so klappt weiss ich noch nicht
self.global_config = config
self.config = ConfigParser.ConfigParser()
self.config.read(CONFIG_FILE)
if self.config:
pos = split(self.config.get('gui', 'wx_friend_pos'), ',')
size = split(self.config.get('gui', 'wx_friend_size'), ',')
wxFrame.__init__(self, parent, ID, title, wxPoint(int(pos[0]), int(pos[1])) , wxSize(int(size[0]), int(size[1])), style)
PSYCPackage.__init__(self)
self.currentBuddy = None
self.center = center
self.model = None
self.packagename = "Friends gui"
# we have to register by ourselves
# perhaps it's not the way to do it but it works
# no.. it's not working.. --lynX
# self.center.register(FriendsPackage(self))
# menubar, statusbar et al
self.CreateStatusBar()
self.SetStatusText("welcome to pyPSYC")
menu = wxMenu()
menu.Append(ID_CONNECT, "&Connect", "connect to the net")
menu.Append(ID_ABOUT, "&About", "tritratrullala")
menu.AppendSeparator()
menu.Append(ID_EXIT, "&Exit", "leave us")
menuBar = wxMenuBar()
menuBar.Append(menu, "&File");
self.SetMenuBar(menuBar)
##'buddy' list, perhaps ;]]
self.BuddyList = wxListCtrl(self, ID_BUDDY_LIST, style=wxLC_REPORT|wxLC_SINGLE_SEL|wxSUNKEN_BORDER)
self.BuddyList.InsertColumn(0, "ST")
self.BuddyList.InsertColumn(1, "Nick")# , wxLIST_FORMAT_RIGHT)
self.BuddyList.SetColumnWidth(0, 20)
##end buddy list
# define the buttons and so on at the bottom of the window
self.status = wxComboBox(self, ID_STATUS, "", choices=["", "This", "is a", "Place", "to put commands"], size=(150,-1), style=wxCB_DROPDOWN)
self.menu_button = wxButton( self, ID_MENU, 'pyPSYC')
self.exit_button = wxButton( self, ID_EXIT, 'exit')
self.con_menu = wxBoxSizer(wxHORIZONTAL)
self.con_menu.Add(self.menu_button, 1, wxALIGN_BOTTOM)
self.con_menu.Add(self.exit_button, 1, wxALIGN_BOTTOM)
sizer = wxFlexGridSizer(3, 0 , 0,0)
sizer.Add(self.BuddyList, 1, wxGROW)
sizer.Add(self.con_menu, 1,wxGROW)
sizer.Add(self.status, 1,wxGROW)
sizer.AddGrowableRow(0)
sizer.AddGrowableCol(0)
# do something so that the buttons don't vanish in a too small window
# this is h4x0r-style but does the job at the moment
sizer.SetItemMinSize(self.BuddyList, 30, 10)
sizer.SetMinSize(wxSize(200,280))
self.SetSizer(sizer)
self.SetAutoLayout(true)
# event handling
EVT_BUTTON( self, ID_EXIT, self.onExit )
EVT_BUTTON( self, ID_MENU, self.doConnect )
#EVT_BUTTON(self, ID_CONNECT, self.doConnect)
#EVT_BUTTON(self, ID_CLOSECHATNOTE, self.closeChat)
EVT_MENU( self, ID_EXIT, self.onExit)
EVT_MENU(self, ID_CONNECT, self.doConnect)
#EVT_LEFT_DCLICK(self.BuddyList, self.onFriend)
#EVT_LIST_ITEM_SELECTED(self, ID_BUDDY_LIST, self.OnBuddySelected)
# ect_activated muss weg sonst wird zweimal nen chat geoeffnet ;]]
##EVT_LIST_ITEM_ACTIVATED(self, ID_BUDDY_LIST, self.onFriend)
def received(self, source, mc, mmp, psyc):
# muss ueberall sein, warum weiss ich noch nicht so genau
# .._present .._absent versteh ich also is es da;]
if mc == "_notice_friend_present":
self.present(mmp.get_source())
elif mc == "_notice_friend_absent":
self.absent(mmp.get_source())
elif mc == "_notice_link" or mc == "_notice_unlink" or mc == "_status_linked":
self.set_status(mc)
def set_model(self, model):
self.model = model
def present(self, nick):
""" do what has to be done if a friend turns up """
# at the moment:
pass
#fuer das x muesste man wissen wie lang die buddy list bisher ist
#sprich vllt doch wieder nen dict dafuer bauen or so
#self.BuddyList.InsertStringItem(x ,'on')
#self.BuddyList.SetStringItem(x, 1, nick)
def absent(self, nick):
""" do what has to be done if a friend leaves us """
# at the moment also:
pass
def set_status(self, status):
""" we should tell the user if the status changes """
#print 'link status: ' + str(status)
def OnBuddySelected(self, event):
self.currentBuddy = event.m_itemIndex
event.Skip()
def doConnect(self, event, host = ''):
self.center.connect(host)
#print 'connecting...'
event.Skip()
def onExit(self, event):
""" do stuff we have to do before exiting """
#print 'exiting...'
pos = str(self.GetPositionTuple()[0]) + ', ' + str(self.GetPositionTuple()[1])
size = str(self.GetSizeTuple()[0]) + ', ' + str(self.GetSizeTuple()[1])
self.config.set('gui', 'wx_friend_pos', pos)
self.config.set('gui', 'wx_friend_size', size)
config_file = file(CONFIG_FILE, 'w')
self.config.write(config_file)
config_file.close()
self.model.set_mc("_request_execute")
self.model.set_target(self.model.center.ME())
self.model.set_text("/quit")
self.model.send()
self.Close(TRUE)
#sys.exit(0)
event.Skip()
class UeberGui(wxFrame):
def __init__(self, parent=NULL, ID=-1, title='uebergui teil', status_tab = 1, pos=wxDefaultPosition, size=wxDefaultSize, style=wxDEFAULT_FRAME_STYLE):
self.config = ConfigParser.ConfigParser()
self.config.read(CONFIG_FILE)
if self.config:
pos = split(self.config.get('gui', 'wx_room_pos'), ',')
size = split(self.config.get('gui', 'wx_room_size'), ',')
wxFrame.__init__(self, parent, ID, title, wxPoint(int(pos[0]), int(pos[1])) , wxSize(int(size[0]), int(size[1])), style)
#PSYCPackage.__init__(self)
self.notebook = wxNotebook(self, ID_NOTEBOOK)
self.tabs = [] # ein nummer zu context mapping
self.querys = []
self.buffers = {} # context : "chatter going on in this context"
self.tab_inst = {} # context : tab instanz
self.query_inst = {} # context : tab instanz
self.members = {} # dictionary a la { 'PSYC' : ['fippo', 'lethe', ...]}
if status_tab == 1:
self.addTab('status')
EVT_TEXT_ENTER(self, ID_SAY, self.say)
EVT_NOTEBOOK_PAGE_CHANGED(self, ID_NOTEBOOK, self.OnTabChange)
EVT_CLOSE(self, self.onClose )
EVT_TEXT(self, ID_SAY, self.EvtText)
def EvtText(self, event):
#print 'EvtText: %s' % event.GetString()
event.Skip()
def find_nick(self, wiesel):
positve_members= []
members = self.members[self.model.get_context()]
for member in members:
member = strip(member)
if member.startswith(wiesel):
positve_members.append(member)
return positve_members
def EvtChar(self, event):
#print 'EvtChar: ' + str(event.GetKeyCode())
if event.GetKeyCode() == 9:
# hier muessen wir nick raten und so weiter
wiesel = self.tab_inst[self.model.get_context()].get_text()
wiesel2 = wiesel
wiesel = split(wiesel, ' ')
marder = self.find_nick(wiesel[-1])
if len(marder) > 1:
print 't1'
self.tab_inst[self.model.get_context()].append_text(str(marder) + '\n')
elif len(marder) == 1:
print 't2'
nick = marder[0]
text = wiesel2
pos = - len(wiesel[-1])
text = text[:pos] + nick
self.tab_inst[self.model.get_context()].clear_entry()
self.tab_inst[self.model.get_context()].append_entry(text)
else:
print 't3'
else:
# hier passen wir einfach und reset'en den coutner
self.dep_count = 0
event.Skip()
def onClose(self,event):
print 'UeberGui.onCLose() worked'
def say(self, event):
""" wird gerufen wenn jmd enter drückt """
text = event.GetString()
#text = text.encode('iso-8859-1')
if text != '' and text[0] == '/':
# we have a command
# if we know the command, we set the appropiate mc
# else we do _request_execute
if text.startswith("/join") and text.__len__() > 16:
# 16 == len(/join psyc://h/@r)
self.model.set_mc("_request_enter")
self.model.set_target(text.split(" ")[1])
self.model.send()
elif text.startswith("/part"):
# wie waers mit /part_logout, part_home, part_type, ...
self.model.set_mc("_request_leave")
self.model.set_target(self.model.get_context())
self.model.send()
elif text.startswith("/quit"):
self.model.set_mc("_request_execute")
self.model.set_target(self.model.center.ME())
self.model.set_text("/quit")
self.model.send()
elif text.startswith('/me') and text[3] == ' ':
self.model.set_target(self.model.get_context())
self.model.set_mc('_message_public')
self.model.set_psycvar('_action', text[4:])
self.model.set_text('')
self.model.send()
elif text.startswith('/names'):
self.model.set_target(self.model.get_context())
self.model.set_mc('_request_members')
self.model.set_text('')
self.model.send()
#elif text.startswith('/lnames'):
# context = self.model.get_context()
# self.tab_inst[context].append_text(self.members[context][0] + '\n')
elif text.startswith("/connect"):
foo = len(text.split(" "))
if foo == 2:
self.model.center.connect(text.split(" ")[1])
elif foo == 3:
self.model.center.connect(text.split(" ")[1], text.split(" ")[2])
else:
self.model.set_target(self.model.center.ME())
self.model.set_mc("_request_execute")
self.model.set_text(text)
self.model.send()
elif text != '' and text[0] == "#":
self.model.set_target(self.model.center.ME())
self.model.set_mc("_request_execute")
self.model.set_text("/" + text[1:])
self.model.send()
elif text != '' and text[0] == "!":
self.model.set_target(self.model.get_context())
self.model.set_mc("_request_execute")
self.model.set_text(text)
self.model.send()
elif text != '' and text[-1] == '?':
self.model.set_target(self.model.get_context())
self.model.set_mc("_message_public")
self.model.set_text(text)
self.model.send()
elif text != '':
#print "msg to", self.model.get_context()
self.model.set_target(self.model.get_context())
self.model.set_mc("_message_public")
self.model.set_text(text)
self.model.set_psycvar('_action', 'testet')
self.model.send()
self.tab_inst[self.model.context].clear_entry()
event.Skip()
def addTab(self, title):
panel = Tab(title, self.notebook)
if title != 'status':
short_title= title[rfind(title, '@'):]
self.model.set_context(title)
panel.append_text('heading over to ' + title + '\n')
self.model.set_target(self.model.get_context())
self.model.set_mc("_request_members")
self.model.set_text('')
self.model.send()
else:
short_title = title
self.notebook.AddPage(panel, short_title, select = 1)
self.tab_inst[title] = panel
self.SetTitle(title)
EVT_CHAR(panel.entry_box, self.EvtChar)
if title == 'status':
motd = 'Welcome to ' + VERSION + '\n for Q&A go to psyc://ve.symlynx.com/@PSYC \n or contact tim@trash-media.de for GUI problems \n for all the other problems go and talk to fippo!'
panel.append_text(motd)
self.tabs.append(title)
#self.buffers[title] = ''
def addQuery(self, title):
panel = Tab(title, self.notebook)
if title != 'status':
short_title= title[rfind(title, '~'):]
#self.model.set_context(title)
print dir(self.model)
panel.append_text('talking to ' + short_title + '\n')
#self.model.set_target(self.model.get_context())
#self.model.set_mc("_request_members")
#self.model.set_text('')
#self.model.send()
else:
short_title = title
self.notebook.AddPage(panel, short_title, select = 1)
self.query_inst[title] = panel
self.SetTitle(title)
if title == 'status':
motd = 'Welcome to ' + VERSION + '\n for Q&A go to psyc://ve.symlynx.com/@PSYC \n or contact tim@trash-media.de for GUI problems \n for all the other problems go and talk to fippo!'
panel.append_text(motd)
self.querys.append(title)
self.source = self.querys[-1]
#print (self.querys)
#print '<<<<<<<<<<<<<<'
#self.buffers[title] = ''
def OnTabChange(self, event):
old = event.GetOldSelection()
new = event.GetSelection()
self.SetTitle(self.tabs[new])
self.model.set_context(self.tabs[new])
#if self.buffers[self.model.context] != '':
# self.tab_inst[self.model.context].append_text(self.buffers[self.model.context])
# self.buffers[self.model.context] = ''
event.Skip()
def msg(self, text):
""" mehr text! """
class UserGui(UeberGui, AbstractGui.UserGui, PSYCPackage):
def __init__(self):
""" das hier is fuer querys """
PSYCPackage.__init__(self)
UeberGui.__init__(self, status_tab = 0)
self.model = None
self.source = ''
#pass
def say(self, event):
text = event.GetString()
#source self.tabs[]
source = self.source
self.model.set_target(source)
self.model.set_mc("_message_private")
self.model.psyc._assign("_nick", get_user(self.model.center.ME()))
#text = self.get_input(self.windows[source]["entryfield"])
self.model.set_text(text.strip())
#self.append_text(self.windows[source]["displayfield"], "Du sagst: " + text)
self.model.send()
self.query_inst[source].append_text('Du> ' + text + '\n')
self.query_inst[source].clear_entry()
event.Skip()
def received(self, source, mc, mmp, psyc):
context = 'status'
if mmp._query("_source") != None:
context = mmp._query("_source").lower()
else:
context = source.lower()
if mc == "_internal_message_private_window_popup":
# open a new tab
self.Show(True)
self.addQuery('test')
elif mc == "_message_echo_private":
self.Show(True)
text = ''
text += 'Du>'
if context not in self.querys:
self.addQuery(context)
text += ' ' + parsetext(mmp, psyc)
self.query_inst[context].append_text(text + '\n')
elif mc =='_message_private':
self.Show(True)
text = ''
text += 'Anderer_Nick>'
if context not in self.querys:
self.addQuery(context)
text += ' ' + parsetext(mmp, psyc)
self.query_inst[context].append_text(text + '\n')
def set_model(self, model):
self.model = model
def OnTabChange(self, event):
old = event.GetOldSelection()
new = event.GetSelection()
self.SetTitle(self.querys[new])
self.source = self.querys[new]
#self.model.set_context(self.tabs[new])
#if self.buffers[self.model.context] != '':
# self.tab_inst[self.model.context].append_text(self.buffers[self.model.context])
# self.buffers[self.model.context] = ''
event.Skip()
class RoomGui(UeberGui, AbstractGui.RoomGui, PSYCPackage):
def __init___(self):
""" das hier is fuer raeume """
PSYCPackage.__init__(self)
UeberGui.__init__(self,pos=pos, size=size)
self.model = None
EVT_CLOSE(self, self.onClose)
def onClose(self,event):
#print 'RoomGui.onClose() worked'
pos = str(self.GetPositionTuple()[0]) + ', ' + str(self.GetPositionTuple()[1])
size = str(self.GetSizeTuple()[0]) + ', ' + str(self.GetSizeTuple()[1])
self.config.set('gui', 'wx_room_pos', pos)
self.config.set('gui', 'wx_room_size', size)
config_file = file(CONFIG_FILE, 'w')
self.config.write(config_file)
config_file.close()
#self.Show(False)
event.Skip()
def set_model(self, model):
self.model = model
def received(self, source, mc, mmp, psyc):
context = 'status'
if mmp._query("_context") != None:
context = mmp._query("_context").lower()
else:
context = source.lower()
#if psyc._query('_nick_place') != None:
# context = 'psyc://ve.symlynx.com/@' + psyc._query('_nick_place').lower()
#context = source.lower()
if mc.startswith('_notice_place_leave'):
if context not in self.tabs:
self.addTab(context)
text = parsetext(mmp, psyc)
self.tab_inst[context].append_text(text + '\n')
if self.members.has_key(context):
nick = psyc._query('_nick')
self.members[context].remove(nick)
if mc.startswith('_notice_place_enter'):
self.Show(true)
if context not in self.tabs:
self.addTab(context)
text = parsetext(mmp, psyc)
self.tab_inst[context].append_text(text + '\n')
if self.members.has_key(context):
nick = psyc._query('_nick')
if nick not in self.members[context]:
self.members[context].append(nick)
elif mc == '_message_public_question':
text = ''
text += str(psyc._query("_nick"))
if context not in self.tabs:
self.addTab(context)
text += ' fragt ' + parsetext(mmp, psyc)
self.tab_inst[context].append_text(text + '\n')
elif mc.startswith('_status_place_topic') or mc.startswith('_notice_place_topic'):
topic = psyc._query('_topic')
context = source.lower()
if context not in self.tabs:
self.addTab(context)
self.tab_inst[context].set_topic(topic)
elif mc == '_message_public':
text = ''
text += str(psyc._query("_nick"))
if context not in self.tabs:
self.addTab(context)
if psyc._query("_action"):
ptext = parsetext(mmp, psyc)
if ptext == '':
text += " " + psyc._query("_action")
else:
text += " " + psyc._query("_action") + '> ' + ptext
else:
text += "> " + parsetext(mmp, psyc)
#text = text.encode('iso-8859-1')
self.tab_inst[context].append_text(text + '\n')
#print '!!' + context + ': ' + text
elif mc == '_status_place_members':
text = parsetext(mmp, psyc)
if context not in self.tabs:
self.addTab(context)
members = split(psyc._query('_members'), ', ')
self.members[context] = members
self.tab_inst[context].append_text(text + '\n')
else:
# everything we don't know goes into the status tab
# hopefully parsetext doesn't crash with' bogus' pakets
text = source.lower() + ' >>> '
text += parsetext(mmp, psyc)
self.tab_inst['status'].append_text(text + '\n')
class MainWindow(PSYCPackage):
# eigentlich brauch das kein schwein oder???
def __init__(self, argv):
""" was ich hiermit mache weiss ich noch net genau, eigentlich brauch
ich es net im moment lebt es für den devel handler"""
self.center = None
PSYCPackage.__init__(self)
def title(self, text):
pass
def run(self):
pass
def quit(self):
self.center.quit()
sys.exit(0)
def connect(self):
pass
def write(self, text):
#print text
pass
class MySplashScreen(wxSplashScreen):
def __init__(self, argv, center, config):
self.center = center
self.argv = argv
self.config = config
bmp = wxImage(opj("GUI/wx/psych2o.jpg")).ConvertToBitmap()
wxSplashScreen.__init__(self, bmp,
wxSPLASH_CENTRE_ON_SCREEN|wxSPLASH_TIMEOUT,
4000, None, -1,
style = wxFRAME_NO_TASKBAR|wxSTAY_ON_TOP)
EVT_CLOSE(self, self.OnClose)
def OnClose(self, evt):
frame = FriendList(NULL, -1, "pyPSYC 0.0.0.0.0.1", center=self.center, config=self.config)
frame.Show()
evt.Skip()
class Application(wxApp):
def __init__(self, argv, center, config):
self.center = center
self.argv = argv
self.config = config
wxApp.__init__(self,0)
#print 'tt'
def OnInit(self):
#self.frame = FriendList(NULL, -1, "pyPSYC 0.0.0.0.0.1", center=self.center, config=self.config)
#self.frame.Show(true)
#sys.stdout = self.frame
wxInitAllImageHandlers()
splash = MySplashScreen(self.argv, self.center, self.config)
self.SetTopWindow(splash)
splash.Show()
return True
##
## self.timer = wxPyTimer(self.socket_check)
## self.timer.Start(100) # alle 100 ms
## #print 'ttt'
##
## def socket_check(self):
## asyncore.poll(timeout=0.0)
##
def run(self):
# blah mainloop
#print 'tttt'
from twisted.internet import wxsupport, reactor
wxsupport.install(self)
print "running reactor..."
reactor.run()

BIN
GUI/wx/devGui.pyc Normal file

Binary file not shown.

BIN
GUI/wx/psych2o.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

7
GUI/wx/test.py Normal file
View File

@ -0,0 +1,7 @@
## some sort of testing thingie
## damit man es sieht wies geht udn die Gui.py auch sonst importieren kann
import Gui
app = Gui.Application(0)
app.MainLoop()

24
INSTALL.txt Normal file
View File

@ -0,0 +1,24 @@
pyPSYC depends on python (tested with 2.2.1) and tkinter
(note: if you want you can write qt, gtk, wxpython gui's etc)
also, this version depends on twisted python from twistedmatrix.com
prior to starting you need to edit the config-example file and move it
to $HOME/.pypsyc/config
on windows keep it in the same directory as chat_client.py and change
line 22
config.read(os.getenv("HOME") + "/.pypsyc/config")
to
config.read("config")
This will soon be automated somehow.
start pyPSYC with `python chat_client.py`
there are two command styles... local command which start with "/"
currently there are two of them, "/join psyc://host/@room"
(atm this works only if you are connected to host)
and "/part" which parts the current room.
/quit is also supported, although atm it is semantically an alias for #quit
the other command style is commands starting with "#"
they are handled by your UNI with a _request_execute

26
LICENSE.txt Normal file
View File

@ -0,0 +1,26 @@
pyPSYC, a multiplexing PSYC client written in Python
Copyright (C) 2002, 2003 by Philipp "fippo" Hancke
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Taken from lynx license for the psyced:
SPECIAL ENCOURAGEMENTS FOR PSYC DEVELOPERS.
1. Whenever you make derivates of this library publically available, be it
in source or binary form, please keep all PSYC network transactions
according to the current specification of the PSYC protocol as available
from http://www.psyc.eu. This will help keeping PSYC implementations
interoperable.
2. Whenever you release a derivative work of this software, please inform
the PSYC community of the existence of your new software by promoting it
on http://about.psyc.eu.

4
Makefile Normal file
View File

@ -0,0 +1,4 @@
# toplevel makefile
clean :
find . -name "*~" | xargs rm
find . -name "*.pyc" | xargs rm

6
README.txt Normal file
View File

@ -0,0 +1,6 @@
Obtain the newest version by git...
git clone git://git.psyced.org/pypsyc
See also http://about.psyc.eu/pypsyc

55
README.wx Normal file
View File

@ -0,0 +1,55 @@
WELCOME TO pyPSYC(with a funky wxWindows GUI)
bugs & black-mail to tim@rash-media.de
-> HOWTO TURN THE BEAST ALIVE
* install wxPython from www.wxpython.org
* extract the tar
* edit config-example and put it into ~/.pypsyc/config
* edit wx-config-example and put it into ~/.pypsyc/wx-config only copying it
there should be enough and you shouldn't edit it afterwards, it's only a
palce for the gui to remeber things like size, position and so on
* windows users should only rename the two files, instead of moving them:
config-example -> config
wx-config-example -> wx-config
* windows users have to edit chat_client.py to make sure it looks for the
config file in the right place and you have to edit the GUI/wx/devGui.py
file also.
* you now should be setup and ready to explore the world of psyc
-> OFFICIAL NOTES
* you need wxPython 2.4.1 and Python 2.2 other versions might also work but
who knows
-> INOFFICIAL NOTES
* versions of wxPython <2.4.1 have a nasty bug that prevents the msg window
from scrolling so you should upgrade
* we have a nick completer now!!! shout out loud: "Hurraayy!"
* bribe marder if you want a shiny-one-click windows installer
* if you don't like the default speak action look into devGui.yp and change
if, shoudl be quite easy
-> STATUS OF THE BEAST
* there are reports that pyPSYC crashes with win2k, we are looking into that
* querys don't work
* you can't close the tabs ;]]

15
config-example Normal file
View File

@ -0,0 +1,15 @@
# should be changed to support http://www.psyc.eu/storage
[main]
uni: psyc://host/~user
password: geheim
[packages]
friends: enabled
conferencing: disabled
biff: disabled
user: disabled
devel: enabled
[gui]
type: wx

99
console-client.py Executable file
View File

@ -0,0 +1,99 @@
#!/usr/bin/env python
#
# broken - fix this some
#
import asyncore
import ConfigParser
import os
import sys
# here we start importing our own modules
from pypsyc.PSYC.PSYCMessagecenter import PSYCMessagecenter
from pypsyc.PSYC import parsetext
# import packages
from pypsyc.PSYC.PSYCRoom import Authentication as AuthenticationPackage
#import Listener
debug=0
class stdin_channel (asyncore.file_dispatcher):
def __init__(self):
asyncore.file_dispatcher.__init__(self, 0)
self.buffer = ""
def handle_read(self):
data = self.recv(512)
self.buffer += data
lines = self.buffer.split("\n")
for line in lines[:-1]:
self.lineReceived(line)
self.buffer = lines[-1]
def handle_close(self):
try:
self.close()
except:
pass
def lineReceived(self, line):
print "line:", line # overridden
def writable(self):
return 0
def log(self, *ignore):
pass
from pypsyc.PSYC.PSYCRoom import PSYCPackage
class stdin_client(stdin_channel, PSYCPackage):
def __init__(self):
stdin_channel.__init__(self)
PSYCPackage.__init__(self)
self.methods = ["devel"]
self.packagename = "pyPSYC console client"
def received(self, source, mc, mmp, psyc):
if debug:
#print self.packagename, "handling", mc, "from", source
print ""
print "mc:", mc
for (key, val) in mmp.thisState.items():
print "MMP:", key, "=>", val
for (key, val) in psyc.thisState.items():
print "PSYC:", key, "=>", val
print parsetext(mmp, psyc)
if mc == "_echo_logoff" or mc == "_status_unlinked":
print "Bye bye."
# eh? shouldn't this clean up and exit? --lynX
sys.exit()
def set_mc(self, mc): self.psyc.set_mc(mc)
def set_target(self, target): self.mmp._assign("_target", target)
def set_text(self, text):
self.psyc.reset_text()
self.psyc.append_text(text)
def send(self):
self.center.send(self.mmp, self.psyc)
# this shouldn't need to be done here
self.mmp.reset_state()
self.psyc.reset_state()
# after each send the temporary vars must be reset
# to the permanent var state automatically. -lynX
def lineReceived(self, line):
self.mmp._set("_source_identification", self.center.ME())
self.set_target(self.center.ME())
self.set_mc("_request_input")
self.set_text(line)
self.send()
config = ConfigParser.ConfigParser()
config.read(os.getenv("HOME") + "/.pypsyc/config")
console = stdin_client()
center = PSYCMessagecenter(config)
center.register(AuthenticationPackage(config))
center.register(console)
center.connect()
asyncore.loop()

24
fippos-twisted/README Normal file
View File

@ -0,0 +1,24 @@
pyPSYC - asyncore based library for PSYC
The application programming interface (API) of this library is stable,
says fippo. But he left it unmaintained since 2005.
News at http://about.psyc.eu/pypsyc
Download by anonCVS from the same server as psyced.
:pserver:anonymous@cvs.psyced.org:/CVS/anoCVS
QUESTIONS?
/tell psyc://psyced.org/~fippo or come to psyc://psyced.org/@welcome
AUTHORS
Philipp Hancke
<psyc://psyced.org/~fippo>
Tim Head
<psyc://psyced.org/~betatim>
Andreas Neue
<psyc://psyced.org/~an>

View File

@ -0,0 +1,230 @@
# usage: twistd -noy ircd.py
import sys
from twisted.application import service, internet
from pypsyc.center import ServerCenter
from pypsyc.net import PSYCServerFactory
from pypsyc.objects import PSYCReceiver
from pypsyc.objects.server import Person, Place, GroupSlave
from pypsyc import parseURL
from twisted.protocols import irc
from twisted.internet.protocol import ServerFactory
"""
watch out, the main purpose of this thing is to debug the context slave
system
"""
class IRCD(irc.IRC, PSYCReceiver):
center = None
password = ''
nick = ''
url = ''
source = None
def __init__(self, center, hostname):
self.center = center
self.hostname = hostname
def connectionMade(self):
peer = self.transport.getPeer()
self.source = 'object:%s:%d'%(peer.host, peer.port)
self.center.register_object(self.source, self)
def connectionLost(self, reason):
if self.url:
self.irc_QUIT(None, None)
elif self.nick is '':
print 'closing unknown connection'
def sendNumeric(self, numeric, msg):
self.sendLine(':%s %s %s :%s'%(self.hostname, numeric, self.nick, msg))
# def sendLine(self, line):
# irc.IRC.sendLine(self, line.encode('iso-8859-1'))
def sendMessage(self, source, target, cmd, data = None):
s = ':%s %s %s'%(self.url2prefix(source), target, cmd)
if data:
s += ' :%s'%data
self.sendLine(s)
def sNotice(self, data):
self.sendLine(':%s NOTICE %s :%s'%(self.hostname, self.nick, data))
"""helper functions"""
def expandUrl(self, target):
"""quick and dirty is_uniform check"""
if target.find(':') is -1:
if target[0] == '#':
return self.hostname + '/@%s'%target[1:]
return self.hostname + '/~%s'%target
if target[0] == '#':
return target[1:]
return target
def minimizeUrl(self, source):
if source.startswith(self.hostname):
# remark: +2 to skip trailing /@
return source[len(self.hostname) + 2:]
return source
def url2prefix(self, url):
if url.find(':') != -1:
u = parseURL(url)
if u['resource'][0] == '~':
ident = u['resource'][1:]
else:
ident = '*'
host = u['host']
else:
ident = url # its a local nick
host = 'localuser'
return '%s!%s@%s'%(url, ident, host)
"""irc_ command hooks"""
def irc_USER(self, prefix, params):
pass
def irc_PASS(self, prefix, params):
self.password = params[0]
def irc_NICK(self, prefix, params):
self.nick = params[0]
self.center.msg({'_source' : self.source,
'_target' : self.expandUrl(self.nick),
'_password' : self.password},
'_request_link', '')
def irc_PRIVMSG(self, prefix, params):
target = params[0]
mc = '_message_private'
if target[0] == '#':
mc = '_message_public'
self.center.msg({ '_source' : self.source,
'_target' : self.expandUrl(target),
'_nick' : self.nick},
mc, params[-1])
def irc_JOIN(self, prefix, params):
chan = params[0]
self.center.msg({ '_source' : self.source,
'_target' : self.expandUrl(chan),
'_nick' : self.nick},
'_request_enter', '')
def irc_PART(self, prefix, params):
chan = params[0]
self.center.msg({ '_source' : self.source,
'_target' : self.expandUrl(chan),
'_nick' : self.nick},
'_request_leave', '')
def irc_QUIT(self, prefix, params):
self.center.msg({ '_source' : self.source,
'_target' : self.url },
'_request_unlink', '')
def irc_unknown(self, prefix, command, params):
if command == 'ROSTER':
self.center.msg({ '_source' : self.source,
'_target' : self.url },
'_request_roster', '')
elif command == 'FRIEND':
self.center.msg({ '_source' : self.source,
'_target' : self.expandUrl(params[0]) },
'_request_friendship', 'Lass uns Freunde sein')
else:
print 'unknown irc cmd %s'%command
"""pypsyc msg API"""
def msgUnknownMethod(self, vars, mc, data):
print 'unsupported %s from %s'%(mc, vars['_source'])
def msg_notice_link(self, vars, mc, data):
self.url = vars['_source']
self.sendNumeric(irc.RPL_WELCOME, 'Hello, %s'%self.nick)
self.sendNumeric(irc.RPL_YOURHOST, 'Welcome to %s'%self.hostname)
self.sendNumeric(irc.RPL_MYINFO, '%s is a pyPSYC daemon IRC interface'%self.hostname)
def msg_message_private(self, vars, mc, data):
if vars['_target'] is self.source:
t = self.nick
else: # should not happen
raise
# TODO: might be appropriate to use self.privmsg()
# self.privmsg(vars['_source'], self.nick, None, data)
s = self.minimizeUrl(vars['_source'])
self.sendMessage(s, 'PRIVMSG', t, data)
def msg_message_echo_private(self, vars, mc, data):
pass # echo is not common in irc
def msg_message_public(self, vars, mc, data):
if vars['_source'] != self.url: # skip echo
if vars.has_key('_context'):
t = '#' + self.minimizeUrl(vars['_context'])
else:
t = '#' + self.minimizeUrl(vars['_source'])
s = self.minimizeUrl(vars['_source'])
# TODO: might be appropriate to use self.privmsg()
# self.privmsg(vars['_source'], None, t, data)
self.sendMessage(s, 'PRIVMSG', t, data)
else:
pass # echo is not common in IRC
def msg_echo_place_enter(self, vars, mc, data):
t = '#' + self.minimizeUrl(vars['_source'])
self.sendMessage(self.nick, 'JOIN', t)
def msg_echo_place_leave(self, vars, mc, data):
t = '#' + self.minimizeUrl(vars['_source'])
self.sendMessage(self.nick, 'PART', t)
def msg_status_place_members(self, vars, mc, data):
t = '#' + self.minimizeUrl(vars['_source'])
self.names(self.nick, t, map(self.minimizeUrl, vars['_list_members']))
def msg_notice_unlink(self, vars, mc, data):
if vars['_source'] == self.url:
self.url = None
self.transport.loseConnection()
def msg_notice_place_enter(self, vars, mc, data):
s = vars['_source']
c = '#' + self.minimizeUrl(vars['_context'])
if s == self.url:
return # we dont like being joined via notice!
self.join(self.url2prefix(self.minimizeUrl(s)), c)
def msg_notice_place_leave(self, vars, mc, data):
s = vars['_source']
c = '#' + self.minimizeUrl(vars['_context'])
self.part(self.url2prefix(self.minimizeUrl(s)), c)
def msg_notice_roster(self, vars, mc, data):
friends = vars['_list_friends']
places = vars['_list_places']
if friends:
self.sNotice('Friends')
for friend in vars['_list_friends']:
self.sNotice('~ %s'%friend)
if places:
self.sNotice('Places')
for place in places:
self.sNotice('@ %s'%place)
def msg_request_friendship(self, vars, mc, data):
sni = self.minimizeUrl(vars['_source'])
self.notice(sni, self.nick,
'%s wants to be your friend'%(sni))
def msg_notice_friendship_established(self, vars, mc, data):
sni = self.minimizeUrl(vars['_source'])
self.notice(sni, self.nick,
'%s is now your friend'%(sni))
class IRCDFactory(ServerFactory):
center = None
def __init__(self, center, location):
self.center = center
self.location = location
def buildProtocol(self, addr):
p = IRCD(self.center, self.location)
p.factory = self
return p
class MyServerCenter(ServerCenter):
def create_user(self, netname):
return Person(netname, self)
def create_place(self, netname):
return Place(netname, self)
def create_context(self, netname):
return GroupSlave(netname, self)
root = 'psyc://ente' # TODO: this does belong into a config file!
application = service.Application('psycserver')
center = MyServerCenter(root)
factory = PSYCServerFactory(center, None, root)
psycServer = internet.TCPServer(4404, factory)
ircfactory = IRCDFactory(center, root)
ircServer = internet.TCPServer(6667, ircfactory)
myService = service.IServiceCollection(application)
psycServer.setServiceParent(myService)
ircServer.setServiceParent(myService)

View File

@ -0,0 +1,9 @@
Purpose:
this is designed to be a news distributing server only. It fetches RSS feeds
Running:
twistd -noy rss_server.py
This code depends on
- rss.py from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/277099
- feedparser.py from http://diveintomark.org/projects/feed_parser/
- twisted python (python is less fun without)

View File

@ -0,0 +1,131 @@
# usage: twistd -noy rss_server.py
from twisted.application import service, internet
from twisted.internet import reactor
from pypsyc.center import ServerCenter
from pypsyc.net import PSYCServerFactory
from pypsyc.objects.server import Place
from pypsyc import parseUNL
try:
from rss import FeederFactory
except ImportError:
print 'error while importing rss.py'
print 'make sure you have rss.py from ',
print 'from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/277099'
print 'and feedparser.py from ',
print 'http://diveintomark.org/projects/feed_parser/'
class PlaceFeeder(FeederFactory):
def getFeeds(self):
return []
class Feedplace(Place):
channel = None
silent = True
def __init__(self, netname, center, feed):
Place.__init__(self, netname, center)
self.feed = feed
self.error = 0 # number of times that we didnt succeed
self.fetched = 0 # number of times we fetched sucessfully
self.fetched_items = 0 # the average number of new items per fetch
self.fetch_interval = 15 * 60 # initial feed interval
self.feeder = PlaceFeeder(False)
self.news = []
reactor.callLater(5, self.fetchFeed)
def fetchFeed(self):
d = self.feeder.start([(self.feed, '')])
d.addCallback(self.gotFeed)
d.addErrback(self.gotError)
def gotError(self, error):
self.error += 1
# TODO: react on feeds that are temp/perm unreachable
reactor.callLater(self.fetch_interval, self.fetchFeed)
print 'looks as if feed %s is unreachable'%self.feed
print error
def gotFeed(self, data):
self.fetched += 1
new = []
items = {}
if self.channel is None:
self.channel = data['channel']
self.castmsg({ '_nick' : self.netname,
'_topic' : self.showTopic()},
'_status_place_topic',
'Topic by [_nick]: [_topic]')
for item in data['items']:
# diff by url
href = item['link']
new.append(href)
items[href] = item
diff = filter(lambda x: x not in self.news, new)
for href in diff:
item = items[href]
v = {'_news_headline' : item['title_detail']['value'],
'_page_news' : href,
'_channel_title' : data['channel']['title'] }
self.castmsg(v, '_notice_news_headline_rss',
'([_channel_title]) [_news_headline]\n[_page_news]')
self.news = new
# feeds whose average number of new items is < x
# can be polled with less frequency
self.fetched_items += len(diff)
avg = float(self.fetched_items) / self.fetched
print 'avg no of new items per fetch for %s is %f'%(self.feed, avg)
if avg < 1.5: # x
# lower frequency
self.fetch_interval *= avg
elif avg > 4.5 and self.fetched > 10: # y
# increase frequenzy
self.fetch_interval /= 2
print 'callLater in %d'%(self.fetch_interval)
reactor.callLater(self.fetch_interval, self.fetchFeed)
def showMembers(self):
return []
def showTopic(self):
if self.channel is not None:
return 'feed \'%s\' available from %s'%(self.channel['title'],
self.feed)
else:
return 'stand by while fetching feed %s'%self.feed
def msg_message_public(self, vars, mc, data):
pass # they're not for talking
class MyServerCenter(ServerCenter):
feeds = {}
def create_user(self, netname):
return False
def create_place(self, netname):
u = parseUNL(netname)
res = u['resource'][1:]
if self.feeds.has_key(res):
return Feedplace(netname, center, self.feeds[res])
return False
def create_context(self, netname):
return False
def setFeeds(self, feeds):
self.feeds = feeds
def getFeeds(self):
return self.feeds
root = 'psyc://ente'
application = service.Application('psyc news distributor')
center = MyServerCenter(root)
factory = PSYCServerFactory(center, None, root)
psycServer = internet.TCPServer(4404, factory)
center.setFeeds({ 'heise' : 'http://www.heise.de/newsticker/heise.rdf' })
myService = service.IServiceCollection(application)
psycServer.setServiceParent(myService)

View File

@ -0,0 +1,28 @@
"""p2p manager and client as described in the old PSYC whitepaper at
http://psyc.pages.de/whitepaper/
probably broken currently"""
from pypsyc.center import ServerCenter, ClientCenter
from pypsyc.objects.PSYCObject import PSYCClient
from pypsyc.objects.Advanced import AdvancedManager, AdvancedPlace
import sys
import asyncore
location = 'psyc://adamantine.aquarium'
type = sys.argv[1]
if type == 'manager':
center = ServerCenter([location + ':4405/', location + ':4406',
location + ':4407', location + ':4408'])
center2 = ServerCenter([location])
AdvancedManager(location + '/@advanced', center2)
if type == 'client':
center = ClientCenter()
me = PSYCClient(location + '/~fippo', center)
me.online()
AdvancedPlace(location + "/@advanced", center)
me.sendmsg({'_target' : location + '/@advanced',
'_source' : location + '/~fippo'},
'_request_enter', '')
while center:
asyncore.poll(timeout=0.5)

10
fippos-twisted/doc/DESIGN Normal file
View File

@ -0,0 +1,10 @@
Design of the pyPSYC library
Asynchronus programming
The role of the center object
Writing a client
- GUI event loop Integration
Writing a server

View File

@ -0,0 +1 @@
onesided zlib compression

View File

@ -0,0 +1,70 @@
from pypsyc.State import State
from twisted.protocols.basic import LineReceiver
class PSYCProtocol(LineReceiver):
statemachine = None
state = 'vars'
mc = None
text = None
delimiter = '\n'
initialized = False
def connectionMade(self):
self.statemachine = State()
self.reset()
self.transport.write('.\n')
def msg(self, vars, mc, text):
"""serialize a packet and send to the other side
@type vars: C{dict}
@param vars: Dictionary of variables to be serialized.
Variables should be strings or lists, variable names start
with an underscore
@type mc: C{str}
@param mc: Methodname of the packet, starts with an underscore
@type text: C{str}
@param text: Data part of the packet"""
packet = self.statemachine.serialize(vars) # this has a newline already!
packet += mc + '\n'
packet += text + '\n'
packet += '.\n'
self.transport.write(packet.encode('iso-8859-1'))
def reset(self):
self.statemachine.reset()
self.state = 'vars'
self.mc = ''
self.text = ''
def lineReceived(self, line):
"""this does not yet handle binary mode and fragments"""
line = line.strip()
if self.initialized is False:
if line != '.':
self.msg({}, '_error_syntax_protocol_initialization',
'The protocol begins with a dot on a line of by itself')
self.transport.loseConnection()
return
else:
self.initialized = True
return
if line == '.':
self.packetReceived()
elif self.state is 'vars' and line.startswith('_'):
self.statemachine.eat(None)
self.mc = line
self.state = 'text'
elif self.state == 'vars':
self.statemachine.eat(line)
else:
self.text += line + '\n'
def packetReceived(self):
vars = self.statemachine.copy()
peer = self.transport.getPeer()
if not vars.has_key('_source'):
vars['_source'] = 'psyc://%s:-%d'%(peer.host, peer.port)
mc = self.mc[:]
data = self.text[:].strip().decode('iso-8859-1')
self.reset()
self.factory.packetReceived(vars, mc, data, self, peer)

View File

@ -0,0 +1,156 @@
# TODO: write tests
from pypsyc import MMPVARS
from copy import deepcopy
class SenderState:
def __init__(self):
self.laststate = {}
self.persistent_out = {}
def serializeList(self, varname, value):
t = []
old = self.persistent_out.get(varname)
# that is far to experimental
if True: # not old: # no previous list sent
self.persistent_out[varname] = value
t.append(':%s\t%s\n'%(varname, value[0].replace('\n', '\n\t')))
for item in value[1:]:
t.append(':\t%s\n'%(item.replace('\n', '\n\t')))
return '\n'.join(t)
if False:
augmented = filter(lambda x: x not in old, value)
if augmented:
packet += '+%s\t%s\n'%(varname, augmented[0].replace('\n',
'\n\t'))
for item in augmented[1:]:
packet += '+\t%s\n'%(item.replace('\n', '\n\t'))
diminished = filter(lambda x: x not in value, old)
if diminished:
packet += '-%s\t%s\n'%(varname, diminished[0].replace('\n',
'\n\t'))
for item in diminished[1:]:
packet += '-\t%s\n'%(item.replace('\n', '\n\t'))
self.persistent_out[varname] = value
def serialize(self, state):
"""
serializes a set of variables into a string
@type state: C{dict}
@param state: Dictionary of variables to be serialized
"""
L = []
# beware of the lambdas!
L.append(self.varencode(filter(lambda x: x[0] in MMPVARS and x[1],
state.items())))
if L != []:
L.append('\n')
L.append(self.varencode(filter(lambda x: x[0] not in MMPVARS and x[1],
state.items())))
self.laststate = state
bytes = '\n'.join(L)
return bytes
def varencode(self, v):
"""
encodes a set of variables, setting the state persistent according to
some strategy
@type v: C{dict}
@param v: Dictionary of variables to be serialized
"""
t = []
for (varname, value) in v:
if self.persistent_out.get(varname) == value:
pass
elif varname.startswith('_list') or type(value) == type([]):
t.append(self.serializeList(varname, value))
elif self.laststate.get(varname) == value and varname != '_context':
self.persistent_out[varname] = value
t.append('=%s\t%s\n'%(varname, value.replace('\n', '\n\t')))
else:
t.append(':%s\t%s\n'%(varname, value.replace('\n', '\n\t')))
return '\n'.join(t)
class ReceiverState:
glyph = ''
varname = ''
listFlag = False
value = ''
def __init__(self):
self.state = {}
self.persistent = {}
def reset(self):
self.state = {}
self.glyph = ''
self.varname = ''
self.listFlag= False
self.value = ''
def copy(self):
# do we actually need those deep copys? TODO
t = deepcopy(self.persistent)
t.update(deepcopy(self.state))
return t
def eat(self, line):
"""
this one is tricky... first it handles the previous line,
and then it prepares the current line.
This is needed to implement multiline-continuations in lists
@type line: C{str} or C{None}
@param line: line to be parsed, a None signals that variable
parsing for current packet is finished
"""
if line: line = line.decode('iso-8859-1') # we use unicode internally
if line and (line[0] == ' ' or line[0] == '\t'): # multiline support
self.value += '\n' + line[1:]
return
# glyph handling
if self.glyph == ':':
if self.listFlag:
if not type(self.state[self.varname]) == list:
self.state[self.varname] = [self.state[self.varname]]
self.state[self.varname].append(self.value)
else:
if self.varname.startswith('_list'):
self.value = [self.value]
self.state[self.varname] = self.value
elif self.glyph == '=':
if self.listFlag:
if not type(self.persistent[self.varname]) == list:
self.persistent[self.varname] = [self.self.persistent[self.varname]]
self.persistent[self.varname].append(self.value)
else:
self.persistent[self.varname] = self.value
elif self.glyph == '+':
self.persistent.get(self.varname, []).append(self.value)
elif self.glyph == '-':
raise NotImplementedError
elif self.glyph == '?':
raise NotImplementedError
if not line: # feeding done
return
# here we parse the current line
self.glyph = line[0]
if line[1] == '\t':
# lastvarname-optimization: varname remains the same
self.listFlag = True
self.value = line[2:]
else:
self.listFlag = False
if line.find('\t') == -1:
self.varname = line[1:]
self.value = ''
else:
self.varname, self.value = line[1:].split('\t', 1)
self.value = self.value
class State(SenderState, ReceiverState):
"""combination of sender and receiver state"""
def __init__(self):
SenderState.__init__(self)
ReceiverState.__init__(self)

View File

@ -0,0 +1,101 @@
"""common methods and constants for pyPSYC"""
# URL parsing functions modelled after psycMUVE parseURL
def parseURL(url):
u = { 'scheme' : '',
'user' : '',
'pass' : '',
'host' : '',
'port' : '4404',
'transport' : '',
'string' : url,
'body' : '',
'userAtHost' : '',
'hostPort' : '',
'root' : '',
'circuit' : '',
'size' : ''
}
if url.find(':') == -1: return u
u['scheme'], t = url.split(':', 1)
if t[0:2] == '//': t = t[2:]
u['body'] = t[:]
if t.find('/') != -1:
t, u['resource'] = t.split('/', 1)
else:
u['resource'] = ''
if u.has_key('resource') and u['resource'].find('#') != -1:
u['resource'], u['fragment'] = u['resource'].split('#', 1)
u['userAtHost'] = t[:]
if t.find('@') != -1:
s, t = t.split('@', 1)
if s.find(':') != -1:
u['user'], u['pass'] = s.split(':', 1)
else:
u['user'] = s
u['hostPort'] = t[:]
u['root'] = u['scheme'] + '://' + u['hostPort']
if t.find(':') != -1:
t, s = t.split(':', 1)
# TODO: split s in Port (numeric), Transport
if s and s[-1] in ['c', 'd', 'm']:
u['transport'] = s[-1]
u['port'] = s[:-1] or '4404'
else:
u['port'] = s or '4404'
u['host'] = t[:]
# print "parseurl(%s)"%url, u
return u
def parseUNL(unl): return parseURL(unl) # alias
def UNL2Location(unl):
# if we did not have the user@host syntax this would
# reduce to a simple splitting in front of #
u = parseUNL(unl)
short = u['scheme'] + '://' + u['host']
if u['port'] != '4404':
short += ':' + u['port']
if u['resource']:
return short + '/' + u['resource']
return short
def netLocation(unl):
u = parseURL(unl)
return u['root']
def parsetext(vars, mc, data, caller=None):
pstring = data
#print '---'
#print type(data)
#print '---'
try:
for (varname, value) in vars.items():
if type(value) == list:
no_list = u''
for x in value:
no_list += x + ', '
pstring = pstring.replace(u'[' + varname + u']', no_list[:-2])
else:
pstring = pstring.replace(u'[' + varname + u']', value)
except:
print 'Error in parsetext() for vars'
return pstring
# debugging helper
def dump_packet(banner, vars, mc, data):
print banner + ' ',
for key in vars.keys():
try:
print key + '=' + vars[key] + ' ',
except:
pass
print mc,
print '[' + parsetext(vars, mc, data) + ']'
# constants
GLYPHS = [':', '=', '+', '-', '?', ' ', '\t' ]
MMPVARS = ["_source", "_target", "_context"]

View File

@ -0,0 +1,269 @@
#!/usr/bin/env python
from pypsyc import parseUNL, UNL2Location, netLocation
from twisted.internet import reactor, defer
from pypsyc.net import PSYCClientConnector, PSYCActiveConnector
from pypsyc import dump_packet
class Center:
object_handlers = {}
remote_connections = {}
def msg(self, vars, mc, data):
raise NotImplementedError
def sendmsg(self, vars, mc, data, target = None):
raise NotImplementedError
def register_object(self, netname, object):
# multiple handlers are not possible
# we only want lower case netnames
self.object_handlers[netname.lower()] = object
def find_object(self, netname):
"""find a local object that corresponds to netname"""
# try "real/local" object"
# we only have lower case netnames
return self.object_handlers.get(netname.lower())
def find_remote(self, netname):
"""find a remote location where netname may be located"""
address = netLocation(netname)
if self.remote_connections.has_key(address):
return self.remote_connections[address]
return None
def register_remote(self, obj, where):
self.remote_connections[where] = obj
def unregister_remote(self, obj, where):
if self.remote_connections.get(where) == obj:
self.remote_connections.pop(where)
def connect(self, location):
raise NotImplementedError
def create_user(self, netname):
raise NotImplementedError
def create_place(self, netname):
raise NotImplementedError
class ClientCenter(Center):
# connection to the homeserver, default return for find_remote
default_connection = None
options = {}
def get_option(self, option):
return self.options.get(option)
def connect(self, location):
# already connected or connecting?
a = self.find_remote(location)
if a:
return a
a = PSYCClientConnector(self, location)
return a.get_queue()
def find_remote(self, netname):
"""clients will send most things via their homeserver
p2p connections are an exception to that, but they will be
created by user objects"""
return Center.find_remote(self, netname) or Center.find_remote(self, self.default_connection)
def msg(self, vars, mc, data):
if not mc:
#print "warning: minor bug in pypsyc: msg() without mc"
return
if self.get_option('debug'):
dump_packet(">>>", vars, mc, data)
source = vars.get('_context') or vars.get('_source')
if not source: return # ignore empty packets?
obj = self.find_object(source)
if obj:
obj.msg(vars, mc, data)
return
print 'unhandled packet from %s'%source
# do something about it... pop up a window, etc
u = parseUNL(source)
if u['resource'] and u['resource'].startswith('~'):
# create a user object
obj = self.create_user(source)
obj.msg(vars, mc, data)
elif u['resource'] and u['resource'].startswith('@'):
# create a place object
obj = self.create_place(source)
obj.msg(vars, mc, data)
else:
print 'no handler for %s object'%(source)
def sendmsg(self, vars, mc, data):
target = vars.get('_target')
if not target: return
obj = self.find_remote(target)
if obj:
obj.msg(vars, mc, data)
else:
raise
# this should not happen!
# TODO this does not belong here
import sha
class Authenticator:
def __init__(self, center, uni, password):
self.center = center
self.uni = uni
self.password = password
def startLink(self):
print 'start link'
self.sendmsg({'_target' : uni }, '_request_link', '')
def msg_query_password(self, vars, mc, data):
if vars['_nonce'] and self.center.get_option('auth_sha'):
digest = sha.sha(vars['_nonce'] + self.password).hexdigest()
self.center.sendmsg({ '_method' : 'sha1',
'_password' : digest },
'_set_password', '')
elif self.center.get_option('auth_plain'):
self.sendmsg({ '_password' : self.password },
'_set_password', '')
else:
print 'no authorization method available!'
return
class Client(ClientCenter):
default_uni = ''
nick = ''
online_callback = None
def __init__(self, config):
self.config = config
self.default_uni = config.get('main', 'uni')
u = parseUNL(self.default_uni)
self.nick = u['resource'][1:]
self.default_connection = netLocation(self.default_uni)
if self.config.has_section('library'):
for option in self.config.options('library'):
self.options[option] = self.config.getboolean('library', option)
def online(self):
self.connect(self.default_connection)
# experimental API using the cool Deferred's
self.online_callback = defer.Deferred()
return self.online_callback
def gotOnline(self):
if self.online_callback:
self.online_callback.callback(None)
print 'connected to host of default location'
def sendmsg(self, vars, mc, data):
if vars.get('_nick') == '':
vars['_nick'] = self.nick
# TODO: I am not sure, it this is correct
if vars.get('_source') == '':
vars['_source'] = self.default_uni
ClientCenter.sendmsg(self, vars, mc, data)
class ServerCenter(Center):
unl2uni = {}
remote_contexts = {}
def __init__(self, location):
self.location = location
def msg(self, vars, mc, data):
if not mc:
return # ignore empty packets
source = vars['_source']
# TODO: auth check 'global' or per-object
if vars.has_key('_identification'):
print 'Identification of %s is %s'%(source, vars['_identification'])
context = vars.get('_context')
if context and not self.is_local_object(context):
target = context
else:
target = vars['_target']
if self.unl2uni.has_key(source):
"""local client who is using us as a proxy"""
self.unl2uni[source].sendmsg(vars, mc, data)
elif target:
obj = self.find_object(target)
if obj:
return obj.msg(vars, mc, data)
# probably object has to be created
if self.is_local_object(target):
u = parseUNL(target)
if (u['resource'] and u['resource'].startswith('@')):
obj = self.create_place(target)
if obj:
return obj.msg(vars, mc, data)
else:
vars['_target'], vars['_source'] = vars['_source'], vars['_target']
self.sendmsg(vars, '_error_unknown_name_place',
'No such place: [_source]')
elif (u['resource'] and u['resource'].startswith('~')):
obj = self.create_user(target)
if obj:
return obj.msg(vars, mc, data)
else:
vars['_target'], vars['_source'] = vars['_source'], vars['_target']
self.sendmsg(vars, '_error_unknown_name_user',
'No such user: [_source]')
elif u['resource']:
vars['_target'] = source
vars['_source'] = target
self.sendmsg(vars, '_error_unknown_name',
'No such object: [_source]')
else: # rootmsg
pass
elif context is not None and self.remote_contexts.has_key(context):
self.remote_contexts[context].castmsg(vars, mc, data)
else: # nonlocal target
print 'rejected relay %s from %s to %s'%(mc, source, target)
return # skip it for now
self.sendmsg({ '_target' : source, '_source' : self.location,
'_destination' : target },
'_error_rejected_relay',
'You are not allowed to send messages to [_destination]')
else:
# no target???
print 'no target in packet???'
def sendmsg(self, vars, mc, data):
target = vars['_target']
if self.is_local_object(target):
self.msg(vars, mc, data)
else:
u = parseUNL(target)
target = (self.find_object(target) or
self.find_remote(target) or
self.find_remote(target))
if not target and u['scheme'] == 'psyc':
q = self.connect(u['root'])
q.msg(vars, mc, data)
elif target:
target.msg(vars, mc, data)
else:
raise 'can not find %s'%(target) # programming error?
def register_object(self, netname, object):
self.object_handlers[netname.lower()] = object
def link_unl(self, unl, uni):
self.unl2uni[unl] = uni
def unlink_unl(self, unl, uni):
if self.unl2uni.has_key(unl) and self.unl2uni[unl] == uni:
self.unl2uni.pop(unl)
def is_local_object(self, location):
return netLocation(location) == netLocation(self.location)
def connect(self, location):
a = PSYCActiveConnector(self, location, self.location)
self.remote_connections[location] = a.get_queue()
return a.get_queue()
def find_remote(self, netname):
address = netLocation(netname)
return self.remote_connections.get(address, None)
def join_context(self, context, obj):
if self.is_local_object(context):
print 'skipping join to local context'
if not self.remote_contexts.has_key(context):
self.remote_contexts[context] = self.create_context(context)
self.remote_contexts[context].join(obj)
def leave_context(self, context, obj):
if self.is_local_object(context):
print 'skipping part of local context'
if self.remote_contexts.has_key(context):
self.remote_contexts[context].leave(obj)
# TODO possibly clean_up that context
def create_context(self, netname):
"""this is how we create context slaves"""
raise NotImplementedError

View File

@ -0,0 +1,186 @@
"""network code for pyPSYC"""
from pypsyc import GLYPHS
from pypsyc.PSYC import PSYCProtocol
from pypsyc import parseUNL, UNL2Location
from twisted.names import client
from twisted.internet import reactor
from twisted.internet.protocol import ClientFactory, ServerFactory
class HostChecker:
"""checks validity of _source/_context in a packet
this could well implement policies like trusted from localhost"""
def __init__(self, center, target, myname):
self.target = target
self.myname = myname
self.center = center
def check(self, vars, mc, data, protocol, peer):
source = vars.get('_context') or vars.get('_source')
if (source and not
source.startswith('psyc://%s'%(peer.host))):
u = parseUNL(source)
d = client.lookupAddress(u['host'])
d.addCallback(self.resolved, peer.host, vars, mc, data, protocol)
elif source:
# numeric address
self.handle(vars, mc, data, protocol)
else:
protocol.msg({ '_source' : self.myname },
'_error_syntax_protocol_missing_source',
'Your implementation is broken')
def handle(self, vars, mc, data, protocol):
method = getattr(self, 'recv%s'%mc, None)
if method is not None:
method(vars, mc, data, protocol)
else:
u = parseUNL(vars['_source'])
self.center.register_remote(protocol, u['root'])
self.center.msg(vars, mc, data)
def resolved(self, record, shouldbe, vars, mc, data, protocol):
v = { '_source' : self.myname,
'_target' : vars['_source'] }
if not record[0]:
print 'name not resolvable'
protocol.msg(v, '_error_rejected_relay',
'[_source] is not resolvable. Goodbye')
protocol.transport.loseConnection()
elif record[0][0].type == 5:
payload = record[0][0].payload
d = client.lookupAddress(payload.name.name)
d.addCallback(self.resolved, shouldbe, vars, mc, data, protocol)
else:
payload = record[0][0].payload
if payload.dottedQuad() == shouldbe:
self.handle(vars, mc, data, protocol)
else:
print 'rejected relay'
protocol.msg(v, '_error_rejected_relay',
'[_source] does not resolve to your ip.')
protocol.transport.loseConnection()
class PSYCConnector:
hostname = ''
port = 4404
factory = None
factory_type = None
real_hostname = ''
def __init__(self, center, target, myname = None):
# TODO: dont try to resolve IP addresses ;)
# must subclass
if not self.factory_type: raise NotImplementedError
try:
self.factory = self.factory_type(center, target, myname)
except:
print self.factory_type
raise
u = parseUNL(target)
self.host = u['host']
d = client.lookupService('_psyc._tcp.' + self.host)
d.addCallback(self.srvResolved)
def get_queue(self):
return self.factory
def resolved(self, record):
if not record[0]:
print 'resolution failed...'
else:
payload = record[0][0].payload
if record[0][0].type == 5: # evil cname
d = client.lookupAddress(payload.name.name)
d.addCallback(self.resolved)
else:
reactor.connectTCP(payload.dottedQuad(), self.port, self.factory)
return True
def srvResolved(self, record):
if not record[0]:
d = client.lookupAddress(self.host)
d.addCallback(self.resolved)
else:
payload = record[0][0].payload
self.port = payload.port
d = client.lookupAddress(payload.target.name)
d.addCallback(self.resolved)
return True
class PSYCClientFactory(ClientFactory):
"""a factory for a client which does not have to check
for validity of host names"""
class PSYCClient(PSYCProtocol):
def connectionMade(self):
PSYCProtocol.connectionMade(self)
self.factory.connectionMade(self)
protocol = PSYCClient
center = None
location = None
def __init__(self, center, location, myname):
self.center = center
self.location = location
def connectionMade(self, proto):
self.center.register_remote(proto, self.location)
self.center.gotOnline()
def packetReceived(self, vars, mc, data, protocol, peer):
self.center.msg(vars, mc, data)
class PSYCClientConnector(PSYCConnector):
factory_type = PSYCClientFactory
class PSYCActiveFactory(ClientFactory, HostChecker):
"""a factory for a client which does hostname checks
maybe this will also become a Q
probably we want to override connectionMade?"""
protocol = PSYCProtocol
queue = []
connected = None
def packetReceived(self, vars, mc, data, protocol, peer):
self.check(vars, mc, data, protocol, peer)
def msg(self, vars, mc, data):
# watch out, if we dont use dict-vars we may need deepcopy
if self.connected is not None:
self.connected.msg(vars, mc, data)
else:
self.queue.append((vars, mc, data))
def run_queue(self, target):
for (vars, mc, data) in self.queue:
target.msg(vars, mc, data)
self.queue = []
self.connected = target
def recv_notice_circuit_established(self, vars, mc, data, protocol):
print 'got _notice_circuit_established'
protocol.msg({'_source' : self.myname,
'_target' : self.target or vars['_source'] },
'_notice_circuit_established',
'hi there')
# TODO: what do we register here? source oder our definition of
# what the source should be (self.target)
self.center.register_remote(protocol, self.target)
self.run_queue(protocol)
class PSYCActiveConnector(PSYCConnector):
factory_type = PSYCActiveFactory
class PSYCServerFactory(ServerFactory, HostChecker):
"""funny question... do we deal all the stuff about
modules, compression, tls etc here?"""
class PSYCServerProtocol(PSYCProtocol):
def connectionMade(self):
peer = self.transport.getPeer()
PSYCProtocol.connectionMade(self)
self.msg({ '_source' : self.factory.myname,
'_target' : 'psyc://%s:-%d'%(peer.host, peer.port)},
'_notice_circuit_established',
'Connection to [_source] established')
protocol = PSYCServerProtocol
def packetReceived(self, vars, mc, data, protocol, peer):
self.check(vars, mc, data, protocol, peer)
def recv_notice_circuit_established(self, vars, mc, data, protocol):
self.center.register_remote(protocol, vars['_source'])

View File

@ -0,0 +1,49 @@
class PSYCReceiver:
def msg(self, vars, mc, data):
'called when a message is received'
# method inheritance
if mc.count('_') > 10:
# considered abusive
return
l = len(mc)
while l > 0:
method = getattr(self, 'msg%s'%(mc[:l]), None)
if method is not None:
# could this method return the next methodname
# to be called? freaky!
# actually, this a much more flexible fallthrough
# than switch/case provides.
# yet it is more expensive to evaluate
method(vars, mc, data)
break
l = mc.rfind('_', 0, l)
else:
self.msgUnknownMethod(vars, mc, data)
def msgUnknownMethod(self, vars, mc, data):
print 'unknown %s'%mc
class PSYCObject(PSYCReceiver):
"""generic PSYC object"""
def __init__(self, netname, center):
self.center = center
self.netname = netname.lower()
self.center.register_object(netname, self)
def url(self):
return self.netname
def str(self):
return self.netname
def sendmsg(self, vars, mc, data):
'called to send a message'
l = len(mc)
while l > 0:
method = getattr(self, 'sendmsg%s'%(mc[:l]), None)
if method is not None:
method(vars, mc, data)
break
l = mc.rfind('_', 0, l)
else:
self.center.sendmsg(vars, mc, data)
def castmsg(self, vars, mc, data):
'called to send a message to a group'

View File

@ -0,0 +1,14 @@
"""Client API for PSYC objects"""
class IPSYCClientObject:
def msg(self, vars, mc, data):
"""receive a message"""
def sendmsg(self, vars, mc, data):
"""send a message to a single person or a group manager
use this make requests to the other side that should not be
distributed in an unchangend fashion"""
def castmsg(self, vars, mc, data):
"""send a message that is destined to be delivered to a group
Note that you should use this for all communication with the
group, as it enables transparent distribution for both centralistic
and peer2peer scenarios"""

View File

@ -0,0 +1,319 @@
from pypsyc.objects import PSYCObject
from pypsyc import parseUNL
from twisted.internet import defer
class GroupMaster(PSYCObject):
"""
@type members: C{dict}
@ivar members: list of members of the group
"""
def __init__(self, netname, center):
PSYCObject.__init__(self, netname, center)
self.members = {}
self.flat = {}
def sizeof(self):
return len(self.flat)
def remove(self, whom, origin = None):
self.flat.pop(whom, None)
if origin is not None:
self.members.get(origin, {}).pop(whom, None)
if not self.members.get(origin, None):
self.members.pop(origin, None)
else:
self.members.pop(whom, None)
def insert(self, whom, origin = None, data = None):
if not data: data = True
self.flat[whom] = data
if origin and not self.netname.startswith(origin):
if not self.members.get(origin):
self.members[origin] = {}
self.members[origin][whom] = True
else:
self.members[whom] = True
def getData(self, whose):
return self.flat[whose]
def setData(self, whose, data):
self.flat[whose] = data
def member(self, who):
return who in self.flat
def castmsg(self, vars, mc, data):
vars['_nick_place'] = 'muh' # IMHO _nick_place is superflous
vars['_context'] = self.netname
for (route, list) in self.members.items():
# TODO: deepcopy needed?
vars['_target'] = route
self.center.sendmsg(vars, mc, data)
class GroupSlave:
def __init__(self, netname, center):
self.center = center
self.context = netname
self.members = {}
def join(self, obj):
url = obj.url()
self.members[url] = obj
# TODO: install a watcher on memberobj?
def leave(self, obj):
url = obj.url()
if url in self.members:
self.members.pop(url)
def castmsg(self, vars, mc, data):
for (target, obj) in self.members.items():
vars['_target'] = target
obj.msg(vars, mc, data)
class Place(GroupMaster):
silent = False
def __init__(self, netname, center):
self.idcallbacks = {}
self.identifications = {}
self.reverseidentifications = {}
GroupMaster.__init__(self, netname, center)
def sendmsg(self, vars, mc, data):
# things like setting vars['_nick_place']
vars['_source'] = self.netname
t = self.reverseidentifications.get(vars['_target'])
if t:
print 'setting target from %s to %s'%(vars['_target'], t)
vars['_target'] = t
GroupMaster.sendmsg(self, vars, mc, data)
def showMembers(self):
return self.flat.keys()
def showTopic(self):
return ''
def msg(self, vars, mc, data):
if '_context' in vars:
print '%s got %s with context %s from %s, bad' % (self.netname, mc,
vars['_context'],
vars['_source'])
return
ident = vars.get('_identification')
source = vars['_source']
if ident and self.identifications.get(source) == ident:
print 'ident of %s is %s'%(source,
self.identifications[vars['_source']])
vars['_source'] = ident
vars.pop('_identification')
GroupMaster.msg(self, vars, mc, data)
def msg_error_invalid_authentication(self, vars, mc, data):
print 'invalid auth'
d = self.idcallbacks.pop((vars['_location'], vars['_source']), None)
if d:
d.errback(1)
def msg_notice_authentication(self, vars, mc, data):
print 'valid auth'
d = self.idcallbacks.pop((vars['_location'], vars['_source']), None)
if d:
self.identifications[vars['_location']] = vars['_source']
self.reverseidentifications[vars['_source']] = vars['_location']
d.callback(1)
def helper(self, res, vars, mc, data):
self.msg(vars, mc, data)
def msg_request_enter(self, vars, mc, data):
source = vars['_source']
if '_identification' in vars:
ident = vars.get('_identification')
print 'looking up identification %s'%(ident,)
self.sendmsg({ '_target' : ident,
'_location' : source },
'_request_authenticate', 'Is that really you?')
d = defer.Deferred()
d.addCallback(self.helper, vars, mc, data)
d.addErrback(self.helper, vars, mc, data)
self.idcallbacks[(source, ident)] = d
return
# TODO: while it is not the final plan to make this via parseUNL
# it is acceptable for now
origin = parseUNL(source)['root']
v = { '_target' : source,
'_source' : self.netname }
if '_tag' in vars:
v['_tag'] = vars['_tag']
else:
pass
if '_nick' in vars:
v['_nick'] = vars['_nick']
else:
pass
if self.silent is True:
v['_control'] = '_silent'
self.sendmsg(v, '_echo_place_enter',
'[_nick] enters')
v.pop('_control', None)
v.pop('_tag', None)
v['_list_members'] = self.showMembers() + [ source ]
self.sendmsg(v, '_status_place_members', '...')
v.pop('_list_members')
if not self.member(source):
v['_source'] = source
self.castmsg(v, '_notice_place_enter', '[_nick] enters')
self.insert(vars['_source'], origin)
def msg_request_leave(self, vars, mc, data):
source = vars['_source']
# TODO again
origin = parseUNL(source)['root']
v = { '_source' : source }
if self.member(source):
self.castmsg({ '_source' : source }, '_notice_place_leave',
'[_nick] leaves')
self.remove(source)
else: # not a member for whatever reason
self.sendmsg({ '_target' : source}, '_echo_place_leave',
'You are not even a member')
pass
if self.sizeof() == 0:
# empty
pass
def msg_message_public(self, vars, mc, data):
if self.silent is False and self.member(vars['_source']):
self.castmsg(vars, mc, data)
class Person(GroupMaster):
def __init__(self, netname, center):
GroupMaster.__init__(self, netname, center)
self.location = None
self.forward = False
self.places = {}
self.tags = {}
def disconnected(self, prot, loc):
"""client has closed connection"""
self.location = None
print 'should leave', places.keys()
self.center.disconnected(prot, loc)
print self.center.remote_connections
def msg(self, vars, mc, data):
if '_context' in vars and not vars['_context'] in self.places:
# context faking, should be impossible as of now
print 'catching fake context!'
return
# dont forward messages from our location to it...
self.forward = vars['_source'] is not self.location
GroupMaster.msg(self, vars, mc, data)
if self.forward is True and self.location is not None:
vars['_target'] = self.location
self.sendmsg(vars, mc, data)
self.forward = False
# user is offline
def sendmsg(self, vars, mc, data):
# TODO: things like tag-creation for joins belong here
# as well as setting vars['_nick']
if not '_source' in vars or vars['_source'] == self.location:
vars['_source'] = self.netname
if vars['_target'] == self.location:
self.center.sendmsg(vars, mc, data)
else:
GroupMaster.sendmsg(self, vars, mc, data)
def sendmsg_request_enter(self, vars, mc, data):
import random
t = str(random.randint(0, 10000))
self.tags[vars['_target']] = t
vars['_tag'] = t
# tobi says, tags become invalid after a certain amount of time
self.center.sendmsg(vars, mc, data)
def sendmsg_request_leave(self, vars, mc, data):
# prepare to leave context after a reasonable amount of time
self.center.sendmsg(vars, mc, data)
def msgUnknownMethod(self, vars, mc, data):
if not self.location:
print 'person:unknown %s for offline user'%mc
def msg_request_link(self, vars, mc, data):
if vars['_password']:
self.msg_set_password(vars, mc, data)
else:
self.sendmsg({ '_target' : vars['_source'] },
'_query_password', 'Is that really you?')
self.forward = False
def msg_set_password(self, vars, mc, data):
"""note that we have NO authorization"""
# TODO: here we have to use origin
if self.location is not None:
self.sendmsg({ '_target' : self.location},
'_notice_unlink',
'You have just lost the magic feeling.')
self.location = vars['_source']
# TODO: we may need to deregister this thing...
self.center.link_unl(self.location, self)
self.sendmsg({ '_target' : self.location},
'_notice_link', 'You are now connected')
self.forward = False
def msg_request_unlink(self, vars, mc, data):
if vars['_source'] == self.netname: # heh, this one is nifty!
self.sendmsg({'_source' : self.netname,
'_target' : self.location },
'_notice_unlink',
'You have just lost the magic feeling.')
self.location = None
self.center.unlink_unl(self.location, self.netname)
for place in self.places.copy():
self.sendmsg({'_source' : self.netname,
'_target' : place },
'_request_leave_logout',
'l8er')
self.forward = False
def msg_echo_place_enter(self, vars, mc, data):
source = vars['_source']
tag = vars.get('_tag')
if not source in self.tags:
self.forward = False
elif self.tags[source] != tag:
self.tags.pop(source) # wrong tag, you get invalid
self.forward = False
else:
self.places[vars['_source']] = 1
self.center.join_context(source, self)
def msg_notice_place_leave(self, vars, mc, data):
if vars['_source'] == self.netname:
mc = '_echo' + mc[7:]
vars['_source'] = vars['_context']
self.msg(vars, mc, data)
def msg_echo_place_leave(self, vars, mc, data):
place = vars.get('_context') or vars['_source']
if place in self.places:
self.places.pop(place)
self.forward = True
def msg_request_roster(self, vars, mc, data):
print 'Roster: %s'%(self.flat)
self.sendmsg({'_source' : self.netname,
'_target' : self.location,
'_list_places' : self.places,
'_list_friends' : self.flat.keys()},
'_notice_roster',
'Your friends are [_friends], you have entered in [_list_places].')
self.forward = False
def msg_request_friendship(self, vars, mc, data):
"""
Friendship states:
known -> 0
asked from -> 1
offered to -> 2
dual accepted -> 3
"""
source = vars['_source']
if self.member(source) and self.getData(source) == 2:
self.setData(source, 3)
mc = '_notice_friendship_establshed'
# TODO
else:
self.insert(source, None, 1)
self.forward = True
def sendmsg_request_friendship(self, vars, mc, data):
target = vars['_target']
if self.member(target) and self.getData(target) == 1:
self.setData(target, 3)
mc = '_notice_friendship_established'
# TODO: echo_notice_friendship_established
else:
self.insert(target, None, 2)
self.center.sendmsg(vars, mc, data)
self.forward = True
def msg_notice_friendship_established(self, vars, mc, data):
source = vars['_source']
if self.member(source) and self.getData(source) == 2:
self.setData(source, 3)

54
fippos-twisted/test.py Normal file
View File

@ -0,0 +1,54 @@
#!/usr/bin/env python
from pypsyc import parseUNL, UNL2Location, netLocation
from pypsyc.objects.PSYCObject import PSYCQueueObject, PSYCObject, PSYCUNI, PSYCPlace, PSYCClient, ClientUser, ClientPlace, AdvancedManager, AdvancedPlace
from pypsyc.net import PSYCUDPSender
from pypsyc.center import ServerCenter, ClientCenter
import asyncore
if __name__ == '__main__':
import sys
type = sys.argv[1]
location = 'psyc://adamantine.aquarium'
center = None
if type == "server":
# tcp only server
center = ServerCenter([location + ':c'])
PSYCPlace(location + '/@place', center)
PSYCUNI(location + '/~fippo', center)
if type == "server2":
# tcp and udp server on non-standard port
center = ServerCenter([location + ':4405'])
center.connect(location)
if type == "udpserver":
center = ServerCenter([location + ':4405d'])
PSYCUNI(location + ':4405/~fool', center)
if type == "udpclient":
# this should better be done via a Center which can parse
# URLs and them handle according to their transport
# but for a quick udp sender this is okay...
q = PSYCUDPSender(location + ':4405d')
q.msg({'_target' : 'psyc://adamantine.aquarium:d/~fippo'},
{'_nick' : 'udpclient'},
'_message_private',
'hallo udp welt')
if type == "client":
center = ClientCenter()
PSYCObject('psyc://adamantine.aquarium', center)
# maybe add config information here?
# and let this thing connect as well?
me = PSYCClient('psyc://adamantine.aquarium/~fippo', center)
me.online()
# but thats the fast way to do it
me.sendmsg({'_target' : 'psyc://adamantine.aquarium/~fippo'},
{'_password' : 'xfippox'},
'_request_link',
'')
while center:
asyncore.poll(timeout=0.5)

66
gtkpypsyc/client.py Normal file
View File

@ -0,0 +1,66 @@
import gtk
import ConfigParser
import os, sys
import encodings.iso8859_1, encodings.ascii, encodings.cp1250, encodings.utf_8, encodings.utf_16
from pypsyc.center import Client
from tabs.place import PlaceTab
from tabs.user import UserTab
from tabs.rss import RSSTab
class GTKClient(Client):
def __init__(self, config):
Client.__init__(self, config)
self.places_window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.places_window.connect('delete_event',
lambda w, e: gtk.main_quit())
self.notebook = gtk.Notebook()
self.notebook.set_tab_pos(gtk.POS_BOTTOM)
self.places_window.add(self.notebook)
#self.places_window.show_all()
def show(self):
self.places_window.show_all()
return gtk.FALSE
def create_place(self, netname):
place = PlaceTab(netname, self)
self.notebook.append_page(place, gtk.Label(netname))
self.notebook.show_all()
return place
def poll():
import asyncore
asyncore.poll(timeout = 0.0)
gtk.timeout_add(250, poll) # 0.25 secs
if __name__ == '__main__':
import sys
config = ConfigParser.ConfigParser()
if sys.platform == 'win32':
os.environ['PATH'] += ';lib;'
config.read('.pypsycrc')
else:
config.read(os.getenv('HOME') + '/.pypsycrc')
center = GTKClient(config)
splash = gtk.Window()
splash.set_decorated(gtk.FALSE)
img = gtk.Image()
img.set_from_file(config.get('interface',
'splashscreen'))
splash.add(img)
img.show()
splash.show()
gtk.timeout_add(3500, splash.hide)
gtk.timeout_add(4000, center.show)
rsstab = RSSTab('psyc://adamantine.aquarium/@heise', center)
center.notebook.append_page(rsstab, gtk.Label('heise rss'))
usertab = UserTab('psyc://adamantine.aquarium/~fool', center)
center.notebook.append_page(usertab, gtk.Label('user dialog'))
center.online()
poll()
gtk.main()

67
gtkpypsyc/demo.py Normal file
View File

@ -0,0 +1,67 @@
#!/usr/bin/envv python
import gtk
import gobject
class UI:
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.netobjects = {}
self.model = gtk.TreeStore(gtk.gdk.Pixbuf,
gobject.TYPE_STRING,
gobject.TYPE_BOOLEAN)
self.view = gtk.TreeView(self.model)
self.view.set_headers_visible(gtk.FALSE)
pxrenderer = gtk.CellRendererPixbuf()
renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn()
column.pack_start(pxrenderer, gtk.FALSE)
column.pack_end(renderer, gtk.TRUE)
column.set_attributes(pxrenderer, pixbuf=0, visible=2)
column.set_attributes(renderer, text=1)
self.view.append_column(column)
self.view.show()
vbox = gtk.VBox()
vbox.show()
vbox.pack_start(self.view)
combo = gtk.Combo()
# TODO: disable editing of current entry, even cursor
combo.set_popdown_strings(['Offline', 'Online'])
combo.entry.set_editable(gtk.FALSE)
combo.show()
vbox.pack_end(combo, gtk.FALSE)
self.friendstree = self.model.insert(None, 0, [None, 'Friends', False])
self.placetree = self.model.insert(None, 1, [None, 'Places', False])
self.append_person('fippo')
self.append_place('PSYC')
gtk.timeout_add(1000, self.change_icon, 1)
gtk.timeout_add(3000, self.change_icon, 2)
gtk.timeout_add(5000, self.change_icon, 3)
self.view.expand_all()
self.window.add(vbox)
self.window.show()
self.window.connect('delete_event', lambda e, w: gtk.main_quit())
def append_person(self, name):
self.model.insert(self.friendstree, -1, [None, name, True])
def append_place(self, name):
image = gtk.Image()
image.set_from_file('./pix/place/icon_f01.png')
pxbuf = image.get_pixbuf()
self.netobjects[name] = self.model.insert(self.placetree, -1, [pxbuf, name, True])
def change_icon(self, image):
print 'change', image
return gtk.FALSE # call only once
u = UI()
gtk.mainloop()

71
gtkpypsyc/list.py Normal file
View File

@ -0,0 +1,71 @@
#!/usr/bin/envv python
import gtk
import gobject
class ListWindow:
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.netobjects = {}
self.model = gtk.TreeStore(gtk.gdk.Pixbuf,
str, # _nick_place
str, # _source/target/context
gobject.TYPE_BOOLEAN)
self.view = gtk.TreeView(self.model)
self.view.set_headers_visible(gtk.FALSE)
pxrenderer = gtk.CellRendererPixbuf()
renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn()
column.pack_start(pxrenderer, gtk.FALSE)
column.pack_end(renderer, gtk.TRUE)
column.set_attributes(pxrenderer, pixbuf=0, visible=3)
column.set_attributes(renderer, text=1)
self.view.append_column(column)
self.view.show()
vbox = gtk.VBox()
vbox.show()
vbox.pack_start(self.view)
combo = gtk.Combo()
# TODO: disable editing of current entry, even cursor
# combobox may be false
combo.set_popdown_strings(['Offline', 'Online'])
combo.entry.set_editable(gtk.FALSE)
combo.show()
vbox.pack_end(combo, gtk.FALSE)
self.friendstree = self.model.insert(None, 0, [None, 'Friends', '', False])
self.placetree = self.model.insert(None, 1, [None, 'Places', '', False])
#gtk.timeout_add(1000, self.change_icon, 1)
self.view.expand_all()
self.window.add(vbox)
self.window.show()
#self.window.connect('delete_event', lambda e, w: gtk.main_quit())
# def change_icon(self, image):
# return gtk.FALSE # call only once
def msg(self, vars, mc, data, caller):
if mc.startswith('_notice_place_enter'):
# only if source == self!
image = gtk.Image()
image.set_from_file('./pix/place/icon_f01.png')
self.netobjects[vars['_context']] = self.model.insert(self.placetree, -1,
[image.get_pixbuf(),
vars['_nick_place'],
vars['_context'], True])
return
if mc.startswith('_notice_friend_present'):
image = gtk.Image()
image.set_from_file('./pix/friend/present.png')
self.netobjects[vars['_source']] = self.model.insert(self.friendstree, -1,
[image.get_pixbuf(),
vars['_nick'],
vars['_source'], True])
return

6
gtkpypsyc/rcfile Normal file
View File

@ -0,0 +1,6 @@
[main]
uni: psyc://your.host/~yournick
password: yourpass
[interface]
splashscreen: pix/symlynX.gif

View File

92
gtkpypsyc/tabs/place.py Normal file
View File

@ -0,0 +1,92 @@
import gtk
from pypsyc.objects import PSYCObject
class PlaceTab(gtk.Frame, PSYCObject):
def __init__(self, netname, center):
gtk.Frame.__init__(self, label = netname)
PSYCObject.__init__(self, netname, center)
vbox = gtk.VBox()
self.add(vbox)
self.textview = gtk.TextView()
self.textview.set_cursor_visible(gtk.FALSE)
self.textview.set_wrap_mode(gtk.WRAP_WORD)
self.textview.set_editable(gtk.FALSE)
self.textbuf = self.textview.get_buffer()
hbox = gtk.HPaned()
hbox.pack1(self.textview)
vbox.pack_start(hbox)
# uni, nick
self.nicklist = gtk.ListStore(str, str)
self.nicklist.set_sort_column_id(1, gtk.SORT_ASCENDING)
nickview = gtk.TreeView()
nickview.set_model(self.nicklist)
nickview.set_headers_visible(gtk.FALSE)
col = gtk.TreeViewColumn('Nick', gtk.CellRendererText(), text = 1)
col.set_resizable(gtk.FALSE)
nickview.append_column(col)
hbox.pack2(nickview, shrink=gtk.FALSE, resize=gtk.FALSE)
self.entry = gtk.Entry()
self.entry.set_text('')
self.entry.connect('activate', self.onEntry)
vbox.pack_end(self.entry, gtk.FALSE)
self.show_all()
def append_text(self, text):
iter = self.textbuf.get_end_iter()
text = text.encode('utf-8') + '\n'
self.textbuf.insert(iter, text)
iter = self.textbuf.get_end_iter()
self.textbuf.place_cursor(iter)
mark = self.textbuf.get_insert()
self.textview.scroll_mark_onscreen(mark)
def onEntry(self, widget):
text = self.entry.get_text()
#print self.netname, '>>', text
self.entry.set_text('')
# handle a command... yes this should not be done that way
if text and text[0] == '/':
cmd = text[1:].upper()
if cmd == 'PART':
self.sendmsg({'_target' : self.netname},
{},
'_request_leave',
'')
return
self.sendmsg({'_target' : self.netname},
{},
'_message_public',
text)
def msg(self, vars, mc, data, caller):
if mc == '_message_public':
self.append_text('%s: %s'%(vars['_nick'], data))
return
if mc == '_message_public_question':
self.append_text('%s %s: %s'%(vars['_nick'],
'fragt',
data))
return
if mc == '_message_public_text_action':
self.append_text('%s %s: %s'%(vars['_nick'],
vars['_action'],
data))
return
if mc == '_status_place_members':
# _list_members, _list_members_nicks
for i in range(0, len(vars['_list_members'])):
self.nicklist.append((vars['_list_members'][i],
vars['_list_members_nicks'][i]))
return
if mc.startswith('_notice_place_leave'):
return
if mc.startswith('_notice_place_enter'):
self.nicklist.append((vars['_source'], vars['_nick']))
return
PSYCObject.msg(self, vars, mc, data, caller)

65
gtkpypsyc/tabs/rss.py Normal file
View File

@ -0,0 +1,65 @@
import gtk
from pypsyc.objects import PSYCObject
class RSSTab(gtk.Frame, PSYCObject):
def __init__(self, netname, center):
import gobject
gtk.Frame.__init__(self, label = netname)
PSYCObject.__init__(self, netname, center)
vbox = gtk.VPaned()
self.add(vbox)
self.label = gtk.Label()
self.model = gtk.ListStore(str, str, str)
self.tree = gtk.TreeView(self.model)
# demos
self.model.append(['Handy-Flatrate von DoCoMo in Japan'.encode('utf-8'),
'http://heise.de/newsticker/meldung/44883',
'Ab Sommer 2004 will auch DoCoMo den Japanern für umgerechnet 31 Euro im Monat eine mobile Flatrate anbieten. mehr...' ])
renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn("Headline", renderer, text=0)
self.tree.append_column(column)
self.tree.connect('row-activated', self.preview)
s1 = gtk.ScrolledWindow()
s1.add(self.tree)
s1.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
vbox.pack1(s1, gtk.TRUE)
self.textview = gtk.TextView()
self.textview.set_cursor_visible(gtk.FALSE)
self.textview.set_wrap_mode(gtk.WRAP_WORD)
self.textview.set_editable(gtk.FALSE)
self.textbuf = self.textview.get_buffer()
s2 = gtk.ScrolledWindow()
s2.add(self.textview)
s2.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
vbox.pack2(s2)
#vbox.pack_end(self.textview, gtk.FALSE)
self.show_all()
def msg(self, vars, mc, data, caller):
if mc == '_status_place_description_news_rss':
self.label.set_label(data)
self.show_all()
return
if mc == '_notice_news_headline_rss':
renderer = gtk.CellRendererText()
text = vars.get('_news_headline').encode('utf-8')
column = gtk.TreeViewColumn(vars.get('_news_headline'), renderer, text=1)
self.tree.append_column(column)
self.show_all()
return
PSYCObject.msg(self, vars, mc, data, caller)
def preview(self, widget, path, column):
model, iter = self.tree.get_selection().get_selected()
self.set_text(model.get_value(iter, 1) + '\n' + model.get_value(iter, 2))
def set_text(self, text):
text = text.encode('utf-8')
self.textbuf.set_text(text)

30
gtkpypsyc/tabs/user.py Normal file
View File

@ -0,0 +1,30 @@
import gtk
from pypsyc.objects import PSYCObject
class UserTab(gtk.Frame):
def __init__(self, netname, center):
gtk.Frame.__init__(self, label = netname)
self.textview = gtk.TextView()
self.textview.set_cursor_visible(gtk.FALSE)
self.textview.set_wrap_mode(gtk.WRAP_WORD)
self.textview.set_editable(gtk.FALSE)
self.textbuf = self.textview.get_buffer()
s = gtk.ScrolledWindow()
s.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
s.add(self.textview)
pane = gtk.VPaned()
#pane.pack1(s)
h = gtk.VBox()
pane.pack2(h, shrink=gtk.FALSE, resize = gtk.FALSE)
pane.pack1(s)
box = gtk.HBox()
box.pack_start(gtk.Button(label='spacing'), expand=gtk.FALSE)
h.pack_start(box, expand=gtk.FALSE)
self.entryfield = gtk.Entry()
h.pack_start(self.entryfield, expand=gtk.FALSE)
self.add(pane)
self.show_all()

70
gui-client.py Executable file
View File

@ -0,0 +1,70 @@
#!/usr/bin/env python
# get stuff above at twistedmatrix.com
import ConfigParser
import os
import sys
# here we start importing our own modules
from pypsyc.PSYC.PSYCMessagecenter import PSYCMessagecenter
# import packages
from pypsyc.PSYC.PSYCRoom import Conferencing as ConferencingPackage
from pypsyc.PSYC.PSYCRoom import Friends as FriendsPackage
from pypsyc.PSYC.PSYCRoom import User as UserPackage
from pypsyc.PSYC.PSYCRoom import Authentication as AuthenticationPackage
from pypsyc.PSYC.PSYCRoom import Devel as DevelPackage
#import Listener
# for linux/posix this should work
CONFIG_FILE = os.getenv("HOME") + "/.pypsyc/config"
# windows users should uncomment the next line and comment the one above
# CONFIG_FILE = 'config'
config = ConfigParser.ConfigParser()
config.read(CONFIG_FILE)
center = PSYCMessagecenter(config)
gui = None
Gui = None
try:
guitype = config.get("gui", "type")
if guitype == "Tkinter":
import GUI.Tkinter.Gui as Gui
elif guitype == "Qt":
import GUI.Qt.Gui as Gui
elif guitype == 'wx':
import GUI.wx.devGui as Gui
if Gui:
if guitype == 'wx':
gui = Gui.Application(sys.argv, center, config)
else:
gui = Gui.Application(sys.argv, center)
## hier muss man besser entscheiden, was ein Toplevel() und was ein Tk() ist!
if config.get("packages", "conferencing") == "enabled":
conferencing_gui = Gui.RoomGui()
center.register(ConferencingPackage(conferencing_gui))
if config.get("packages", "friends") == "enabled":
friendlist = Gui.FriendList()
center.register(FriendsPackage(friendlist))
if config.get("packages", "user") == "enabled":
usergui = Gui.UserGui()
center.register(UserPackage(usergui))
if config.get("packages", "devel") == "enabled":
debuggui = Gui.MainWindow(center)
debuggui.title("debug window")
center.register(DevelPackage(debuggui))
## hier was in der Art von setMainWindow()
except ConfigParser.NoSectionError:
print "Error reading config file"
center.register(AuthenticationPackage(config))
gui.run()

View File

@ -0,0 +1 @@

248
in-silico/gui/displays.py Normal file
View File

@ -0,0 +1,248 @@
# -*- coding: latin-1 -*-
#from wxPython.wx import *
import wx
# XXX Please feel free to modify and vershclimmbesser this piece of code
# especially the bits marked with XXX
##from pypsyc.objects.PSYCObject import
from pypsyc.objects.client import ClientUser, ClientPlace, PSYCClient
from pypsyc.objects import PSYCObject
from pypsyc.center import ClientCenter
from pypsyc import netLocation, parsetext
#from psycObjects import PUser, PPlace, PClient, PServer
import extras
import asyncore, sys, os
ID_ABOUT = 1002
ID_CONNECT = 1001
ID_DISCONNECT = 10011
ID_SAY = 3300
ID_NOTEBOOK = 3301
ID_STATUS = 9901
ID_MENU = 9902
ID_BUDDY_LIST = 9903
ID_BUDDY_LIST_DK = 990301
ID_EXIT = 1099
ID_CLOSECHATNOTEBOOK = 2099
ID_CLOSECHATNOTE = 2098
class wxObject:
def __init__(self, parent, psyc_parent = None):
""" basic display object """
if not psyc_parent: print 'WARNING: no psyc_parent set, this could be a problem'
self.netname = self.psyc_parent.netname
def append1(self, text):
""" use this to append multi/single line text"""
pass
class wxPTab(wx.Panel):
def __init__(self, parent, psyc_parent = None):
""" all das ausehen usw """
wx.Panel.__init__(self, parent, -1, style=wx.NO_BORDER)
# we use logic_parent to call the functions pypsyc provides
# gui stuff in the wx.Blah objects, psyc stuff in the PBlah objects
if not psyc_parent: print 'WARNING: no psyc_parent set, this could be a problem'
self.psyc_parent = psyc_parent
config = self.psyc_parent.center.config
self.prompt = config['prompt']
self.lock = 0
self.counter = 0
self.buffer = ''
self.text_box = wx.TextCtrl(self, -1, style=wx.NO_BORDER|wx.TE_MULTILINE|wx.TE_RICH2|wx.TE_READONLY, size=wx.DefaultSize)
self.entry_box = wx.TextCtrl(self, ID_SAY, style=wx.NO_BORDER|wx.TE_PROCESS_ENTER|wx.TE_RICH2|wx.TE_PROCESS_TAB, size=wx.DefaultSize)
fontcolour = wx.Colour(config['fontcolour'][0], config['fontcolour'][1], config['fontcolour'][2])
bgcolour = wx.Colour(config['bgcolour'][0], config['bgcolour'][1], config['bgcolour'][2])
points = self.text_box.GetFont().GetPointSize() # get the current size
f = wx.Font(points, wx.MODERN, wx.NORMAL, wx.BOLD, False)
if os.name == 'nt':
self.text_box.SetDefaultStyle(wx.TextAttr(fontcolour, bgcolour, f))
self.entry_box.SetDefaultStyle(wx.TextAttr(fontcolour, bgcolour, f))
self.entry_box.SetBackgroundColour(bgcolour)
self.text_box.SetBackgroundColour(bgcolour)
self.SetBackgroundColour(bgcolour)
if os.name == 'posix':
self.text_box.SetDefaultStyle(wx.TextAttr(wx.NullColour, wx.NullColour, f))
self.entry_box.SetDefaultStyle(wx.TextAttr(wx.NullColour, wx.NullColour, f))
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.text_box, 1, wx.EXPAND)
sizer.Add(self.entry_box, 0, wx.EXPAND)
self.SetSizer(sizer)
#print 'wx.PTab: ' + str(dir(self))
wx.EVT_TEXT_ENTER(self, ID_SAY, self.input)
def input(self, event):
text = event.GetString()
text = text.lstrip() # only strip whites from the beginning
mc = ''
vars = {}
data = ''
vars['_target'] = self.psyc_parent.netname # eigentlich is target immer netname
if text != '' and text[0] == '/':
# houston we have a command
if text.startswith("/join"): # and text.__len__() > 16:
mc = '_request_enter'
vars['_target'] = text[6:]
#elif text.startswith("/part"):
# mc = '_request_leave'
# vars['_target'] = text[6:]
elif text.startswith('/password'):
mc = '_set_password'
t = text.split('/password')
if t[1] != '' and t[1] != ' ':
vars['_password'] = t[1]
vars['_target'] = self.psyc_parent.center.config['uni']
else:
self.append1('Usage: /password <secret>')
return
elif text.startswith('/connect'):
self.psyc_parent.center.connect(text[9:])
self.append1('connecting to: ' + text[9:])
self.entry_box.SetValue('')
return
elif text.startswith('/retrieve'):
mc = '_request_retrieve'
vars['_target'] = self.psyc_parent.center.config['uni']
elif text.startswith('/store'):
mc = '_request_store'
vars['_target'] = self.psyc_parent.center.config['uni']
vars['_storic'] = text[7:]
data = text[7:]
else:
vars['_target'] = self.psyc_parent.center.config['uni']
#vars['_source']
mc = '_request_execute'
data = text
elif text!= '' and text.startswith('##debug'):
self.entry_box.SetValue('')
self.append1(str(getattr(self.psyc_parent.center, text[8:])))
return
elif text != '' and text[0] == '#':
self.append1(text)
self.entry_box.SetValue('')
return
else:
mc = '_message_public'
try:
text2 = text.decode('iso-8859-15')
except:
text2 = text
print 'unicode'
data1 = text2.encode('iso-8859-1')
data = data1
# am ende einfach wegschicken das fertige paket
self.entry_box.SetValue('')
#print_psyc(mmp, psyc, mc, data, 'place sending')
self.psyc_parent.sendmsg(vars, mc, data)
def append1(self, line):
""" use this to append multi/single line text"""
if type(line) == type(u'öäü'):
try:
line = line.encode('iso-8859-1')
except:
print 'waahh'
if os.name == 'posix':
if self.lock == 0:
self.lock = 1
if self.buffer != '':
self.text_box.AppendText('buffered: ' + self.buffer)
self.buffer = ''
for linex in line.split('\n'):
self.text_box.AppendText(self.prompt.encode('iso-8859-15') + linex)
self.text_box.AppendText('\n')
self.psyc_parent.center.Yield()
self.lock = 0
else:
self.buffer += line + '\n'
elif os.name == 'nt':
# AppendText() doesn't seem to do the right thing in windows
for linex in line.split('\n'):
self.text_box.WriteText(self.prompt.encode('iso-8859-15') + linex)
self.text_box.WriteText('\n')
# we should find out how many 'lines' we write and scroll the right
# amount instead of scrolling 10000 at once XXX
self.text_box.ScrollLines(10000)
self.psyc_parent.center.Yield()
def append(self, text):
""" use this for more then one line USE append1() this is obsolete!! """
# is this broken? do we have to do the same as in append1()?
lines = text.split('\n')
for line in lines:
self.text_box.AppendText(self.prompt + line + '\n')
#print self.netname + ': ' + str(text)
class wxPFrame(wx.Frame):
def __init__(self, parent = None, psyc_parent = None, title = 'pypsyc-frame', pos = wx.DefaultPosition, size = wx.Size(920, 570), style = wx.DEFAULT_FRAME_STYLE):
wx.Frame.__init__(self, None, -1, title, size=size)
# do we need it here? wx.Frame is only supposed to be a container
# and shouldn't do that much -> only here to be flexible
# we use logic_parent to call the functions pypsyc provides
# gui stuff in the wx.Blah objects, psyc stuff in the PBlah objects
if not psyc_parent: print 'WARNING: no psyc_parent set, this could be a problem'
self.psyc_parent = psyc_parent
self.notebook = wx.Notebook(self, -1, style=0)
self.CreateStatusBar()
self.SetStatusText("welcome to pypsyc")
self.Show()
def addTab(self, tab):
tab.create_display(parent=self.notebook)
self.notebook.AddPage(tab.display['default'], str(tab.netname), 1)
class wxPPlace(wxPTab):
def __init__(self, parent, psyc_parent = None):
wxPTab.__init__(self, parent=parent, psyc_parent=psyc_parent)
self.netname = self.psyc_parent.netname
class wxPUser(wxPTab):
def __init__(self, parent, psyc_parent = None):
wxPTab.__init__(self, parent=parent, psyc_parent=psyc_parent)
self.netname = self.psyc_parent.netname
class wxPClient(wx.Frame):
def __init__(self, parent = None, title = 'pypsyc', psyc_parent = None, pos = wx.DefaultPosition, size = wx.Size(100, 400), style = wx.DEFAULT_FRAME_STYLE):
wx.Frame.__init__(self, None, -1, title, size=size)
if not psyc_parent: print 'WARNING: no psyc_parent set, this could be a problem'
self.psyc_parent = psyc_parent
self.CreateStatusBar()
self.SetStatusText("welcome to pypsyc")
self.BuddyList = wx.ListCtrl(self, 2222, style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.SUNKEN_BORDER)
self.BuddyList.InsertColumn(0, "ST")
self.BuddyList.InsertColumn(1, "Nick")# , wx.LIST_FORMAT_RIGHT)
self.BuddyList.SetColumnWidth(0, 20)
self.status = wx.ComboBox(self, ID_STATUS, "", choices=["Offline", "Online", "Away"], size=(150,-1), style=wx.CB_DROPDOWN)
self.menu_button = wx.Button( self, ID_MENU, 'pypsyc')
self.exit_button = wx.Button( self, ID_EXIT, 'exit')
self.con_menu = wx.BoxSizer(wx.HORIZONTAL)
self.con_menu.Add(self.menu_button, 1, wx.ALIGN_BOTTOM)
self.con_menu.Add(self.exit_button, 1, wx.ALIGN_BOTTOM)
sizer = wx.FlexGridSizer(3, 0 , 0,0)
sizer.Add(self.BuddyList, 1, wx.GROW)
sizer.Add(self.con_menu, 1,wx.GROW)
sizer.Add(self.status, 1,wx.GROW)
sizer.AddGrowableRow(0)
sizer.AddGrowableCol(0)
# do something so that the buttons don't vanish in a too small window
# this is h4x0r-style but does the job at the moment
sizer.SetItemMinSize(self.BuddyList, 30, 10)
sizer.SetMinSize(wx.Size(200,280))
self.SetSizer(sizer)
self.SetAutoLayout(True)
self.Show()

66
in-silico/gui/extras.py Normal file
View File

@ -0,0 +1,66 @@
# -*- coding: latin-1 -*-
def print_psyc(vars, mc, data, caller = ''):
print '------ ' + str(caller)
print '-- mc:'
print mc
print '-- vars:'
print str(vars.items())
print '-- data:'
print str([data])
print '------'
class Context:
class __impl:
#from extras import Config
#def __init__(self):
# self.config = Config()
# self.hust = 'hallo garrit'
hust = 'hallo garrit'
def spam(self):
return id(self)
__instance = __impl()
def __getattr__(self, attr):
return getattr(self.__instance, attr)
def __setattr__(self, attr, value):
return setattr(self.__instance, attr, value)
class DevNull:
def __init__(self):
pass
def write(self, text):
pass
class Config(dict):
def __init__(self, file = None):
self[u'uni'] = u'psyc://ve.symlynx.com/~betatim'
self[u'password'] = u'tim0914'
self[u'action'] = u'brabbel'
self[u'bgcolour'] = (255, 236, 191)
self[u'fontcolour'] = (34, 63, 92)
self[u'fontsize'] = 8
self[u'prompt'] = u'* '
class Display(dict):
""" this dict like object organises multiple displays """
def __init__(self, display = None):
if display:
self['default'] = display
def append1(self, text):
self['default'].append1(text)
t = """def __setitem__(key = None, item = None):
if key and item:
dict.__setitem__(key, item)
elif item:
if self['default']:
print 'Overwrite default display explicitly'
else:
self['default'] = item"""

View File

@ -0,0 +1,297 @@
# -*- coding: latin-1 -*-
#from wxPython.wx import *
import wx
# XXX Please feel free to modify and vershclimmbesser this piece of code
# especially the bits marked with XXX
##from pypsyc.objects.PSYCObject import
from pypsyc.objects.client import ClientUser, ClientPlace, PSYCClient
from pypsyc.objects import PSYCObject
from pypsyc.center import ClientCenter
from pypsyc import netLocation, parsetext, UNL2Location
from displays import wxPFrame, wxPTab, wxPPlace, wxPUser, wxPClient
import asyncore, sys, os
import extras
class PObject(PSYCObject):
def __init___(self, netname, center):
PSYCObject.__init__(self, netname, center)
self.context = extras.Context()
self.display = extras.Display()
self.queue = [] # a place to store pakets
def create_display(self, display=None, parent=None, name = 'default'):
""" create a new display for this object """
# display := display object
# parent := if we don't have a display object we need to create one
# and perhaps need a parent
# name := the display needs a name so we can access it later
if name == 'default':
if self.display['default']:
print 'there is already a default display, \
if you want a new display give it a unique name'
return
else:
if display:
# a display has to have some basic functionality
self.display[name] = display
else:
# we're a bit confused now because we can't know what sort of
# display to create, but perhas someone comes up with a idea later
print 'HELP!'
def msg(self, vars, mc, data, caller):
PSYCObject.msg(self, vars, mc, data, caller)
# store things until we know what do with them
f = (vars, mc, data, caller)
self.queue.append(f)
class wxCenter(wx.App, ClientCenter):
def __init__(self): #vllt noch cmd line args uebergeben or so
wx.App.__init__(self, 0)
#self.run()
def OnInit(self):
ClientCenter.__init__(self)
self.context = extras.Context()
self.context.config = extras.Config()
self.config = self.context.config
self.frame = wxPFrame(psyc_parent=self) # nen frame um die tabs aufzuheben
self.timer = wx.PyTimer(self.socket_check)
self.timer.Start(100) # alle 100 ms
return True
def socket_check(self):
asyncore.poll(timeout=0.0) # das sollte vllt klappen
def run(self):
puni = UNL2Location(self.config['uni'])
self.client = PClient(puni, self)
self.create_server_place(netLocation(self.config['uni']))
# XXX extremly buggy here!!
#self.create_server_place('psyc://adamantine.fippo.int')
self.default_connection = netLocation(self.config['uni'])
self.client.online()
self.client.create_display(self)
self.SetTopWindow(self.client.display['default'])
self.client.display['default'].Show()
self.MainLoop()
def sendmsg(self, vars, mc, data, caller = None):
extras.print_psyc(vars, mc, data, 'center sending')
ClientCenter.sendmsg(self, vars, mc, data, caller)
def set_default_connection(self, uni):
#netLocation(self.uni)
print 'set_default_connect: ' + netLocation(uni)
self.default_connection = netLocation(uni)
def msg(self, vars, mc, data, caller):
source = vars['_context'] or vars['_source']
if not ClientCenter.msg(self, vars, mc, data, caller):
# create a psyc object of unknown type
#self.create_psyc_object(source)
#print '\n\n !!WE ARE IN TROUBLE!!\n\n\n'
print ''
def create_psyc_object(self, netname):
# this could do some better guessing about the type
self.create_server_place(netname)
# XXX
def create_user(self, netname):
t = PUser(netname, self)
#print 'i am a USER and my name is: ' + netname + ' / ' + netLocation(netname)
self.frame.addTab(t)
return t
def create_place(self, netname):
t = PPlace(netname, self)
#print 'i am a PLACE and my name is: ' + netname + ' / ' + netLocation(netname)
self.frame.addTab(t)
return t
def create_server_place(self, netname):
t = PServer(netname, self)
#print 'i am a SERVER and my name is: ' + netname + ' / ' + netLocation(netname)
self.frame.addTab(t)
self.client.create_display(name='server', display=t.display['default'])
#sys.stdout = extras.DevNull()
return t
class PPlace(ClientPlace):
def __init__(self, netname, center):
ClientPlace.__init__(self, netname, center)
self.display = extras.Display()
print 'i am a PLACE and my name is: ' + self.netname + ' / ' + netLocation(self.netname)
def create_display(self, parent = None, name = 'default', display = None):
if name == 'default':
self.display['default'] = wxPPlace(parent=parent, psyc_parent=self)
else:
self.display[name] = display
def msg(self, vars, mc, data, caller = None):
extras.print_psyc(vars, mc, data, 'place')
#print 'ddd' + str(type(data))
parsedtext = parsetext(vars, mc, data)
#print dir(parsedtext) , type(parsedtext)
#parsedtext = parsedtext.encode('iso-8859-15')
#print type(parsedtext)
if mc == '_message_public':
line = u''
if vars['_nick']:
line += vars['_nick']
else:
line += vars['_source']
if vars['_action']: line += ' ' + vars['_action'] + '>'
else: line += '>'
try:
line += ' ' + parsedtext.decode('iso-8859-15')
except:
line += ' ' + parsedtext
self.display.append1(line)
elif mc == '_message_public_question':
line = u''
line += vars['_nick']
if vars['_action']: line += ' ' + vars['_action'] + '>'
else: line += ' fragt>'
try:
line += ' ' + parsedtext.decode('iso-8859-15')
except:
line += ' ' + parsedtext
self.display.append1(line)
elif mc.startswith('_status_place_topic'):
self.display.append1('TOPIC: ' + parsedtext)
else:
self.display.append1(parsedtext)
class PUser(ClientUser):
def __init__(self, netname, center):
ClientUser.__init__(self, netname, center)
self.display = extras.Display()
print 'i am a USER and my name is: ' + self.netname + ' / ' + netLocation(self.netname)
def create_display(self, parent = None, name = 'default', display = None):
if name == 'default':
self.display['default'] = wxPUser(parent=parent, psyc_parent=self)
else:
self.display[name] = display
def msg(self, vars, mc, data, caller = None):
extras.print_psyc(vars, mc, data, caller)
parsedtext = parsetext(vars, mc,data)
#parsedtext = parsedtext.encode('iso-8859-15')
if mc == '_message_private':
self.display.append1(parsedtext)
else:
self.display.append1(parsedtext)
class PServer(PSYCObject):
def __init__(self, netname, center):
PSYCObject.__init__(self, netname, center)
print 'registered server'
#print 'PServer: ' + str(dir(self))
self.display = extras.Display()
print 'i am a SERVER and my name is: ' + self.netname + ' / ' + netLocation(self.netname)
def create_display(self, parent = None, name = 'default', display = None):
if name == 'default':
self.display['default'] = wxPPlace(parent=parent, psyc_parent=self)
else:
self.display[name] = display
def msg(self, vars, mc, data, caller = None):
extras.print_psyc(vars, mc, data, 'server')
parsedtext = parsetext(vars, mc,data)
#parsedtext = parsedtext.encode('iso-8859-15')
#self.center.Yield()
if mc == '_notice_circuit_established' and vars['_source'] == netLocation(self.center.config['uni']):
mc = '_request_link'
vars = {}
data =''
vars['_target'] = self.center.config['uni']
self.sendmsg(vars, mc, data)
self.display.append1(parsedtext)
else:
self.display.append1(parsedtext)
def write(self, text):
""" redirected stdout """
# we have problems if text contains \n and with print adding an extra \n
lines = text.split('\n')
for line in lines:
if line == '':
return
else:
self.display.append1('printed: ' + line)
def sendmsg(self, vars, mc, data, caller = None):
extras.print_psyc(vars, mc, data, 'server sending...')
PSYCObject.sendmsg(self, vars, mc, data, caller)
class PClient(PSYCClient):
def __init__(self, netname, center):
PSYCClient.__init__(self, netname, center)
self.display = extras.Display()
#self.extra_display = {} # XXX
#self.create_display(parent=None)
print 'default_conmnect is --> ' + self.center.default_connection
print 'i am a CLIENT and my name is: ' + self.netname + ' / ' + netLocation(self.netname)
def create_display(self, parent = None, name = 'default', display = None):
if name == 'default':
self.display[name] = wxPClient(parent=parent, psyc_parent=self)
else:
if self.display.has_key(name):
# XXX tempory hack for multi server connect
print 'HACKING GOING ON!'
return
else:
self.display[name] = display
def set_display(self, which, display):
self.display[which] = display
def msg(self, vars, mc, data, caller = None):
extras.print_psyc(vars, mc, data, 'client')
parsedtext = parsetext(vars, mc,data)
#parsedtext = parsedtext.encode('iso-8859-15')
if mc == '_query_password':
if self.center.config['password'] != '':
mc = '_set_password'
vars = {'_password' : self.center.config['password']}
vars['_target'] = self.netname
data =''
self.sendmsg(vars, mc, data)
else:
self.display['server'].append1("Please enter your password or choose a different nick if you don't know the password")
self.display['server'].entry_box.SetValue('/password ')
elif mc == '_status_friends':
self.display['server'].append1(parsedtext)
elif mc == '_error_invalid_password':
self.display['server'].append1(parsedtext)
self.display['server'].append1("Please enter your password or choose a different nick if you don't know the password")
self.display['server'].entry_box.SetValue('/password ')
else:
self.display['server'].append1(parsedtext)
def online(self):
self.center.connect(self.center.default_connection)
#mc = '_request_link'
#mmp = {}
#psyc = {}
#data =''
#mmp['_target'] = self.netname
#self.sendmsg(mmp, psyc, mc, data)

450
in-silico/gui/wx.py Normal file
View File

@ -0,0 +1,450 @@
# -*- coding: latin-1 -*-
from wxPython.wx import *
# XXX Please feel free to modify and vershclimmbesser this piece of code
# especially the bits marked with XXX
##from pypsyc.objects.PSYCObject import
from pypsyc.objects.PSYCObject import ClientUser, ClientPlace, PSYCClient, PSYCObject
from pypsyc.center import ClientCenter
from pypsyc import netLocation, parsetext
import asyncore, sys, os
# ich glaub ich weiss jetzt wofuer die hier toll sind
# !!!
# this list isn't up-to-date anymore, stolen from the old gui code
# !!!
ID_ABOUT = 1002
ID_CONNECT = 1001
ID_DISCONNECT = 10011
ID_SAY = 3300
ID_NOTEBOOK = 3301
ID_STATUS = 9901
ID_MENU = 9902
ID_BUDDY_LIST = 9903
ID_BUDDY_LIST_DK = 990301
ID_EXIT = 1099
ID_CLOSECHATNOTEBOOK = 2099
ID_CLOSECHATNOTE = 2098
def opj(path):
"""Convert paths to the platform-specific separator"""
return apply(os.path.join, tuple(path.split('/')))
def print_psyc(vars, mc, data, caller = ''):
print '------ ' + str(caller)
print '-- mc:'
print mc
print '-- vars:'
print str(vars.items())
print '-- data:'
print str([data])
print '------'
class DevNull:
def write(self, text):
return
class wxCenter(wxApp, ClientCenter):
def __init__(self): #vllt noch cmd line args uebergeben or so
wxApp.__init__(self, 0)
self.run()
def OnInit(self):
ClientCenter.__init__(self)
self.config = {} # = { 'uin' : 'psyc://blah/~user', 'foo': 'bar'}
self.config[u'uni'] = u'psyc://ve.symlynx.com/~betatim3'
self.config[u'password'] = u'tim'
self.config[u'action'] = u'brabbel'
self.config[u'bgcolour'] = (255, 236, 191)
self.config[u'fontcolour'] = (34, 63, 92)
self.config[u'fontsize'] = 8
self.config[u'prompt'] = u'* '
print sys.getdefaultencoding()
print os.name
#print self.config
self.frame = wxPFrame(logic_parent=self) # nen frame um die tabs aufzuheben
self.client = PClient(self.config['uni'], self)
self.create_server_place(netLocation(self.config['uni'])) # eignetlich is es kein place aber naja
self.default_connection = netLocation(self.config['uni'])
self.client.online()
self.client.create_display(self)
self.SetTopWindow(self.client.display)
self.client.display.Show()
self.timer = wxPyTimer(self.socket_check)
self.timer.Start(100) # alle 100 ms
return True
def input(self, event):
print event.GetString()
event.Skip()
def socket_check(self):
asyncore.poll(timeout=0.0) # das sollte vllt klappen
def run(self):
self.MainLoop()
def sendmsg(self, vars, mc, data, caller = None):
print_psyc(vars, mc, data, 'center sending')
ClientCenter.sendmsg(self, vars, mc, data, caller)
def set_default_connection(self, uni):
#netLocation(self.uni)
print 'set_default_connect: ' + netLocation(uni)
self.default_connection = netLocation(uni)
def create_user(self, netname):
#print "this should create a user"
t = PUser(netname, self)
# nicht tab.display damit wir netname haben ,]]
# auch wenn es extrem strange ist wir uebergeben
self.frame.addTab(t)
return t
def create_place(self, netname):
#print "this should create a room"
t = PPlace(netname, self)
self.frame.addTab(t) # same here
return t
def create_server_place(self, netname):
t = PServer(netname, self)
self.frame.addTab(t, extra_display='server') # same here
sys.stdout = t
return t
class wxPTab(wxPanel):
def __init__(self, parent, logic_parent = None):
""" all das ausehen usw """
wxPanel.__init__(self, parent, -1, style=0)
# we use logic_parent to call the functions pypsyc provides
# gui stuff in the wxBlah objects, psyc stuff in the PBlah objects
if not logic_parent: print 'WARNING: no logic_parent set, this could be a problem'
self.logic_parent = logic_parent
config = self.logic_parent.center.config
self.prompt = config['prompt']
self.lock = 0
self.counter = 0
self.buffer = ''
self.text_box = wxTextCtrl(self, -1, style=wxTE_MULTILINE|wxTE_RICH2|wxTE_READONLY, size=wxDefaultSize)
self.entry_box = wxTextCtrl(self, ID_SAY, style=wxTE_PROCESS_ENTER|wxTE_RICH2|wxTE_PROCESS_TAB, size=wxDefaultSize)
fontcolour = wxColour(config['fontcolour'][0], config['fontcolour'][1], config['fontcolour'][2])
bgcolour = wxColour(config['bgcolour'][0], config['bgcolour'][1], config['bgcolour'][2])
self.entry_box.SetBackgroundColour(bgcolour)
self.text_box.SetBackgroundColour(bgcolour)
points = self.text_box.GetFont().GetPointSize() # get the current size
f = wxFont(points, wxMODERN, wxNORMAL, wxBOLD, False)
self.text_box.SetDefaultStyle(wxTextAttr(fontcolour, bgcolour, f))
self.entry_box.SetDefaultStyle(wxTextAttr(fontcolour, bgcolour, f))
self.SetBackgroundColour(bgcolour)
sizer = wxBoxSizer(wxVERTICAL)
sizer.Add(self.text_box, 1, wxEXPAND)
sizer.Add(self.entry_box, 0, wxEXPAND)
self.SetSizer(sizer)
#print 'wxPTab: ' + str(dir(self))
EVT_TEXT_ENTER(self, ID_SAY, self.input)
def input(self, event):
text = event.GetString()
text = text.lstrip() # only strip whites from the beginning
mc = ''
vars = {}
data = ''
vars['_target'] = self.logic_parent.netname # eigentlich is target immer netname
if text != '' and text[0] == '/':
# houston we have a command
if text.startswith("/join"): # and text.__len__() > 16:
mc = '_request_enter'
vars['_target'] = text[6:]
elif text.startswith("/part"):
mc = '_request_leave'
vars['_target'] = text[6:]
elif text.startswith('/password'):
mc = '_set_password'
t = text.split('/password')
if t[1] != '' and t[1] != ' ':
vars['_password'] = t[1]
vars['_target'] = self.logic_parent.center.config['uni']
else:
self.append1('Usage: /password <secret>')
return
elif text.startswith('/retrieve'):
mc = '_request_retrieve'
vars['_target'] = self.logic_parent.center.config['uni']
else:
mc = '_request_execute'
data = text
elif text != '' and text[0] == '#':
self.append1(text)
self.entry_box.SetValue('')
return
else:
mc = '_message_public'
try:
text2 = text.decode('iso-8859-15')
except:
text2 = text
print 'unicode'
data1 = text2.encode('iso-8859-1')
data = data1
# am ende einfach wegschicken das fertige paket
self.entry_box.SetValue('')
#print_psyc(mmp, psyc, mc, data, 'place sending')
self.logic_parent.sendmsg(vars, mc, data)
def append1(self, line):
""" use this to append multi/single line text"""
if type(line) == type(u'öäü'):
line = line.encode('iso-8859-15')
if os.name == 'posix':
if self.lock == 0:
self.lock = 1
if self.buffer != '':
self.text_box.AppendText('buffered: ' + self.buffer)
self.buffer = ''
for linex in line.split('\n'):
self.text_box.AppendText(self.prompt.encode('iso-8859-15') + linex)
self.text_box.AppendText('\n')
self.logic_parent.center.Yield()
self.lock = 0
else:
self.buffer += line + '\n'
elif os.name == 'nt':
# AppendText() doesn't seem to do the right thing in windows
for linex in line.split('\n'):
self.text_box.WriteText(self.prompt.encode('iso-8859-15') + linex)
self.text_box.WriteText('\n')
# we should find out how many 'lines' we write and scroll the right
# amount instead of scrolling 10000 at once XXX
self.text_box.ScrollLines(10000)
self.logic_parent.center.Yield()
def append(self, text):
""" use this for more then one line USE append1() this is obsolete!! """
# is this broken? do we have to do the same as in append1()?
lines = text.split('\n')
for line in lines:
self.text_box.AppendText(self.prompt + line + '\n')
#print self.netname + ': ' + str(text)
class wxPFrame(wxFrame):
def __init__(self, parent = None, logic_parent = None, title = 'pypsyc-frame', pos = wxDefaultPosition, size = wxSize(920, 570), style = wxDEFAULT_FRAME_STYLE):
wxFrame.__init__(self, None, -1, title, size=size)
# do we need it here? wxFrame is only supposed to be a container
# and shouldn't do that much -> only here to be flexible
# we use logic_parent to call the functions pypsyc provides
# gui stuff in the wxBlah objects, psyc stuff in the PBlah objects
if not logic_parent: print 'WARNING: no logic_parent set, this could be a problem'
self.logic_parent = logic_parent
self.notebook = wxNotebook(self, -1, style=0)
self.CreateStatusBar()
self.SetStatusText("welcome to pypsyc")
self.Show()
def addTab(self, tab,extra_display = None):
tab.create_display(self.notebook)
if extra_display:
self.logic_parent.client.set_display(extra_display, tab.display)
self.notebook.AddPage(tab.display, str(tab.netname), 1)
class wxPPlace(wxPTab):
def __init__(self, parent, logic_parent = None):
wxPTab.__init__(self, parent=parent, logic_parent=logic_parent)
self.netname = self.logic_parent.netname
class wxPUser(wxPTab):
def __init__(self, parent, logic_parent = None):
wxPTab.__init__(self, parent=parent, logic_parent=logic_parent)
self.netname = self.logic_parent.netname
class wxPClient(wxFrame):
def __init__(self, parent = None, title = 'pypsyc', logic_parent = None, pos = wxDefaultPosition, size = wxSize(100, 400), style = wxDEFAULT_FRAME_STYLE):
wxFrame.__init__(self, None, -1, title, size=size)
# we use logic_parent to call the functions pypsyc provides
# gui stuff in the wxBlah objects, psyc stuff in the PBlah objects
if not logic_parent: print 'WARNING: no logic_parent set, this could be a problem'
self.logic_parent = logic_parent
self.CreateStatusBar()
self.SetStatusText("welcome to pypsyc")
self.BuddyList = wxListCtrl(self, 2222, style=wxLC_REPORT|wxLC_SINGLE_SEL|wxSUNKEN_BORDER)
self.BuddyList.InsertColumn(0, "ST")
self.BuddyList.InsertColumn(1, "Nick")# , wxLIST_FORMAT_RIGHT)
self.BuddyList.SetColumnWidth(0, 20)
self.status = wxComboBox(self, ID_STATUS, "", choices=["Offline", "Online", "Away"], size=(150,-1), style=wxCB_DROPDOWN)
self.menu_button = wxButton( self, ID_MENU, 'pypsyc')
self.exit_button = wxButton( self, ID_EXIT, 'exit')
self.con_menu = wxBoxSizer(wxHORIZONTAL)
self.con_menu.Add(self.menu_button, 1, wxALIGN_BOTTOM)
self.con_menu.Add(self.exit_button, 1, wxALIGN_BOTTOM)
sizer = wxFlexGridSizer(3, 0 , 0,0)
sizer.Add(self.BuddyList, 1, wxGROW)
sizer.Add(self.con_menu, 1,wxGROW)
sizer.Add(self.status, 1,wxGROW)
sizer.AddGrowableRow(0)
sizer.AddGrowableCol(0)
# do something so that the buttons don't vanish in a too small window
# this is h4x0r-style but does the job at the moment
sizer.SetItemMinSize(self.BuddyList, 30, 10)
sizer.SetMinSize(wxSize(200,280))
self.SetSizer(sizer)
self.SetAutoLayout(true)
self.Show()
class PPlace(ClientPlace):
def __init__(self, netname, center):
ClientPlace.__init__(self, netname, center)
self.display = None
def create_display(self, parent):
self.display = wxPPlace(parent=parent, logic_parent=self)
def msg(self, vars, mc, data, caller = None):
print_psyc(vars, mc, data, 'place')
#print 'ddd' + str(type(data))
parsedtext = parsetext(vars, mc, data)
#print dir(parsedtext) , type(parsedtext)
#parsedtext = parsedtext.encode('iso-8859-15')
#print type(parsedtext)
if mc == '_message_public':
line = u''
line += vars['_nick']
if vars['_action']: line += ' ' + vars['_action'] + '>'
else: line += '>'
try:
line += ' ' + parsedtext.decode('iso-8859-15')
except:
line += ' ' + parsedtext
self.display.append1(line)
elif mc.startswith('_status_place_topic'):
self.display.append1('TOPIC: ' + parsedtext)
else:
self.display.append1(parsedtext)
class PUser(ClientUser):
def __init__(self, netname, center):
ClientUser.__init__(self, netname, center)
self.display = None
def create_display(self, parent):
self.display = wxPUser(parent=parent, logic_parent=self)
def msg(self, vars, mc, data, caller = None):
print_psyc(vars, mc, data, caller)
parsedtext = parsetext(vars, mc,data)
#parsedtext = parsedtext.encode('iso-8859-15')
if mc == '_message_public':
self.display.append1(parsedtext)
else:
self.display.append1(parsedtext)
class PServer(PSYCObject):
def __init__(self, netname, center):
PSYCObject.__init__(self, netname, center)
print 'registered server'
#print 'PServer: ' + str(dir(self))
self.display = None
def create_display(self, parent):
self.display = wxPPlace(parent=parent, logic_parent=self)
def msg(self, vars, mc, data, caller = None):
print_psyc(vars, mc, data, 'server')
parsedtext = parsetext(vars, mc,data)
#parsedtext = parsedtext.encode('iso-8859-15')
#self.center.Yield()
if mc == '_notice_circuit_established':
mc = '_request_link'
vars = {}
data =''
vars['_target'] = self.center.config['uni']
self.sendmsg(vars, mc, data)
self.display.append1(parsedtext)
else:
self.display.append1(parsedtext)
def write(self, text):
""" redirected stdout """
# we have problems if text contains \n and with print adding an extra \n
lines = text.split('\n')
for line in lines:
if line == '':
return
else:
self.display.append1('printed: ' + line)
def sendmsg(self, vars, mc, data, caller = None):
print_psyc(vars, mc, data, 'server sending...')
PSYCObject.sendmsg(self, vars, mc, data, caller)
class PClient(PSYCClient):
def __init__(self, netname, center):
PSYCClient.__init__(self, netname, center)
self.display = None
self.extra_display = {} # XXX
#self.create_display(parent=None)
print 'default_conmnect is --> ' + self.center.default_connection
def create_display(self, parent):
self.display = wxPClient(parent=parent)
def set_display(self, which, display):
self.extra_display[which] = display
def msg(self, vars, mc, data, caller = None):
print_psyc(vars, mc, data, 'client')
parsedtext = parsetext(vars, mc,data)
#parsedtext = parsedtext.encode('iso-8859-15')
if mc == '_query_password':
if self.center.config['password'] != '':
mc = '_set_password'
vars = {'_password' : self.center.config['password']}
vars['_target'] = self.netname
data =''
self.sendmsg(vars, mc, data)
else:
self.extra_display['server'].append1("Please enter your password or choose a different nick if you don't know the password")
self.extra_display['server'].entry_box.SetValue('/password ')
elif mc == '_status_friends':
self.extra_display['server'].append1(parsedtext)
elif mc == '_error_invalid_password':
self.extra_display['server'].append1(parsedtext)
self.extra_display['server'].append1("Please enter your password or choose a different nick if you don't know the password")
self.extra_display['server'].entry_box.SetValue('/password ')
else:
self.extra_display['server'].append1(parsedtext)
def online(self):
self.center.connect(self.center.default_connection)
#mc = '_request_link'
#mmp = {}
#psyc = {}
#data =''
#mmp['_target'] = self.netname
#self.sendmsg(mmp, psyc, mc, data)

8
in-silico/test.py Normal file
View File

@ -0,0 +1,8 @@
from gui.psycObjects import wxCenter
if __name__ == '__main__':
center = wxCenter()
center.run()
#center = wx.wxCenter()
#center.heartbeat()

8
mjacob/TODO.txt Normal file
View File

@ -0,0 +1,8 @@
-"AuthModul" muss erweitert (Passwort) und aus twisted_client.py verschoben
werden
-Ereignisse (bzw. alles außer Nachrichten) werden noch nicht angezeigt
-private messages und in andere Räume joinen funktionieren noch nicht
-Einrichtungsassistent oder etwas in die Richtung wäre nicht schlecht
-Schließen funktioniert nicht (man muss im Moment /quit in den Client, Ctrl-C
im Konsolenfenster eingeben und Fenster schließen)
-noch viel mehr...

0
mjacob/UI/__init__.py Normal file
View File

BIN
mjacob/UI/__init__.pyc Normal file

Binary file not shown.

130
mjacob/UI/wx_.py Executable file
View File

@ -0,0 +1,130 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import wx
from base import Module
from lib import psyctext
from twisted_client import install_wx
class Tab(wx.Panel):
def __init__(self, parent, name):
self.name = name
wx.Panel.__init__(self, parent, -1)
self.textctrl = wx.TextCtrl(self, -1, style = wx.TE_MULTILINE|wx.TE_READONLY)
self.users = []
def OnResize(event):
self.textctrl.SetSize(event.GetSize())
self.Bind(wx.EVT_SIZE, OnResize, self)
class MainWindow(Module, wx.Frame):
methods = ['_message*']
def __init__(self, ui):
wx.Frame.__init__(self, None, -1, size = (800, 600))
self.Bind(wx.EVT_CLOSE, self.OnClose, self)
self.ui = ui
self.tabs = {}
splitter = wx.SplitterWindow(self, -1)
self.pl = wx.Panel(splitter, -1)
sizer = wx.GridBagSizer(0, 0)
self.nb = wx.Notebook(self.pl, -1, style = wx.NB_TOP)
sizer.Add(self.nb, (0, 0), (1, 1), wx.EXPAND)
sizer.AddGrowableCol(0)
sizer.AddGrowableRow(0)
self.input = wx.TextCtrl(self.pl, -1)
sizer.Add(self.input, (1, 0), (1, 1), wx.EXPAND)
self.pl.SetSizerAndFit(sizer)
self.add_tab("psyc://%s" % self.ui.base.config.host)
self.pr = wx.Panel(splitter, -1) # right panel
self.userlist = wx.ListBox(self.pr, -1, (0, 5), style = wx.SUNKEN_BORDER)
self.nb.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnChangeTab, self.nb)
def OnResize(event):
size = event.GetSize()
self.userlist.SetSize((size[0], size[1] - 10))
self.pr.Bind(wx.EVT_SIZE, OnResize, self.pr)
splitter.SplitVertically(self.pl, self.pr, self.GetSize()[0] * 0.7)
splitter.SetSashGravity(1)
mainmenu = wx.Menu()
menuitem = mainmenu.Append(-1, '&Connect', 'Connect to the server')
self.Bind(wx.EVT_MENU, self.OnConnect, menuitem)
menuitem = mainmenu.Append(-1, '&Debug window',
'Open or close debug window')
self.Bind(wx.EVT_MENU, self.OnOpenDebugWindow, menuitem)
mainmenu.AppendSeparator()
menuitem = mainmenu.Append(-1, '&Exit', 'Exit pyPSYC')
self.Bind(wx.EVT_MENU, self.OnClose, menuitem)
menubar = wx.MenuBar()
menubar.Append(mainmenu, '&pyPSYC')
self.SetMenuBar(menubar)
self.input.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.Show(True)
def OnKeyDown(self, event):
if event.GetKeyCode() in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER):
pagename = self.nb.GetCurrentPage().name
value = self.input.GetValue()
if value:
self.center.input(pagename, value)
self.input.SetValue('')
event.Skip()
def OnChangeTab(self, event):
self.userlist.Set(self.nb.GetCurrentPage().users)
def OnConnect(self, event):
self.center.connect(self.center.config.host)
self.ui.connected = True
def OnOpenDebugWindow(self, event):
# open or close window:
if not self.ui.debugwindow.Show(True): self.ui.debugwindow.Show(False)
def OnClose(self, event):
pass
def add_tab(self, name):
self.tabs[name] = Tab(self.nb, name)
self.nb.AddPage(self.tabs[name], name)
def handle_message_public(self, packet, physsource):
self._print(packet.mmpvars['_context'], "%s: %s" % (packet.psycvars['_nick'], psyctext(packet)))
handle_message_echo_public = handle_message_public
def _print(self, window, text):
if window not in self.tabs:
self.add_tab(window)
self.tabs[window].textctrl.AppendText(text + '\n')
class UI:
def __init__(self, base):
self.base = base
self.gui = wx.PySimpleApp()
self.mainwindow = MainWindow(self)
self.base.register_module(self.mainwindow)
install_wx(self.gui)

BIN
mjacob/UI/wx_.pyc Normal file

Binary file not shown.

143
mjacob/base.py Executable file
View File

@ -0,0 +1,143 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
## base.py
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# <C> Copyright 2007, Manuel Jacob
from lib.parse import MMPParser, PSYCParser
from lib.render import MMPRenderer, PSYCRenderer
from lib.packets import MMPPacket, PSYCPacket
debug = False
class Module:
methods = []
def register_center(self, center):
self.center = center
def received(self, packet, physsource):
func = getattr(self, 'handle_%s' % packet.mc[1:], None)
if func:
func(packet, physsource)
else:
self.handle(packet, physsource)
class MMPCircuit:
def __init__(self, center):
self.center = center
self.mmp_parser = MMPParser()
self.mmp_parser.recv_packet = self.recv_packet
self.mmp_renderer = MMPRenderer()
def init(self):
self.send_mmp_packet(MMPPacket())
def recv_packet(self, packet):
self.center.recv_mmp_packet(packet, self)
def send_mmp_packet(self, packet):
self._send(self.mmp_renderer.render(packet))
class PSYCCircuit(MMPCircuit):
def __init__(self, center):
MMPCircuit.__init__(self, center)
self.psyc_parser = PSYCParser()
self.psyc_renderer = PSYCRenderer()
def send_psyc_packet(self, packet):
self.send_mmp_packet(self.psyc_renderer.render(packet))
class Plugin:
def __init__(self, name):
self.name = name
self.p = __import__('plugins.' + self.name, (), (), [self.name])
self.needs = self.p.needs
def load(self):
self.instance = self.p.Module()
class Config:
def __init__(self, app):
self.app = app
self.settings_folder = self.get_settings_folder()
self.configfile = self.open_configfile()
class BaseCenter:
type = 'base'
registered_modules = []
handlers = {}
plugins = {}
connections = {}
def load_plugin(self, name):
p = Plugin(name)
for load in p.needs:
self.load_plugin(load)
p.load()
self.register_module(p.instance)
self.plugins[name] = p
def register_module(self, module):
for i in module.methods:
if i in self.handlers:
self.handlers[i].append(module)
else:
self.handlers[i] = [module]
module.register_center(self)
self.registered_modules.append(module)
def connected(self, connection, host):
self.connections[host] = connection
self._connected(connection, host)
def _connected(self, connection, host):
pass
def recv_mmp_packet(self, packet, physsource): # called by circuit
raise NotImplementedError
def recv_psyc_packet(self, packet, physsource): # called by subclass
handlers = []
for key, value in self.handlers.items():
if key == packet.mc or \
(key[-1] == '*' and packet.mc.startswith(key[:-1])):
handlers.extend(value)
for i in handlers:
i.received(packet, physsource)
def send_mmp_packet(self, packet, physdest):
raise NotImplemented
def _send_psyc_packet(self, packet, physdest):
if debug: # WARNING: Do not show all packets
print """packet to %s::
mmpvars: %s
psycvars: %s
mc: %s
text: %s
""" % (physdest, packet.mmpvars, packet.psycvars, packet.mc, packet.data)
physdest.send_psyc_packet(packet)

BIN
mjacob/base.pyc Normal file

Binary file not shown.

87
mjacob/client.py Executable file
View File

@ -0,0 +1,87 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# <C> Copyright 2007, Manuel Jacob
import os
from base import Config, BaseCenter
from lib.packets import PSYCPacket, MMPPacket
from lib import get_host, get_user
class ClientConfig(Config):
def __init__(self, app):
Config.__init__(self, app)
self.uni_file = os.path.join(self.settings_folder, 'me')
self.clientconfig_file = os.path.join(self.settings_folder, 'pypsyc', 'config')
#self.uni = self.get_uni()
import random
self.uni = 'psyc://beta.ve.symlynX.com/~pypsyctest' + ''.join([random.Random().choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789') for i in [0]*10])
#self.uni = raw_input('uni?: ')
self.host = get_host(self.uni)
self.username = get_user(self.uni)
self.ui = 'wx'
def get_settings_folder(self):
# resolve settings folder
if os.name == 'posix':
folder = os.path.join(os.environ.get('HOME'), ".psyc")
elif os.name == 'nt':
folder = os.path.join(os.environ.get('APPDATA'), 'psyc')
else:
folder = os.path.join(os.getcwd(), 'psyc')
# create settings folder if necessary
try:
os.mkdir(folder)
except OSError:
pass
return folder
def get_uni(self):
f = file(self.uni_file)
uni = f.read().strip()
f.close()
return uni
def open_configfile(self):
return None
class ClientCenter(BaseCenter):
type = 'client'
def __init__(self):
self.config = ClientConfig(self)
def _connected(self, connection, host):
pass
def recv_mmp_packet(self, packet, physsource):
if not packet.vars and not packet.body:
return
psyc_packet = physsource.psyc_parser.parse(packet)
self.recv_psyc_packet(psyc_packet, physsource)
def send_psyc_packet(self, packet):
self._send_psyc_packet(packet, self.connections[self.config.host])
def input(self, pagename, text):
if '@' in pagename:
self.send_psyc_packet(PSYCPacket(mmpvars = {'_target': pagename}, mc = '_message_public', data = text))
if text.startswith('/'):
self.command(pagename, text)
def command(self, pagename, text):
if text == '/quit' or '/bye':
self.send_psyc_packet(PSYCPacket(mmpvars = {'_target': self.config.uni}, mc = '_request_execute', data = text))
else:
self.send_psyc_packet(PSYCPacket(psycvars = {'_focus': pagename}, mc = '_request_input', data = text))

BIN
mjacob/client.pyc Normal file

Binary file not shown.

67
mjacob/lib/__init__.py Executable file
View File

@ -0,0 +1,67 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
## __init__.py
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# <C> Copyright 2007, Manuel Jacob
import re
# the following two functions are from the 'original' pypsyc
# (and slightly modified)
def get_host(uni):
m = re.match("^psyc:\/\/(.+)?\/~(.+)?\/?$", uni)
if m: return m.group(1)
m = re.match("^psyc:\/\/([^\/@]+)\@(.+?)\/?$", uni)
if m: return m.group(2)
m = re.match("^psyc:\/\/(.+)?\/\@(.+)?\/?$", uni)
if m: return m.group(1)
m = re.match("^psyc:\/\/(.+)$", uni)
if m: return m.group(1)
raise "invalid uni"
def get_user(uni):
m = re.match("^psyc:\/\/(.+)?\/~(.+)?\/?$", uni)
if m: return m.group(2)
m = re.match("^psyc:\/\/([^\/@]+)\@(.+?)\/?$", uni)
if m: return m.group(1)
raise "invalid uni"
def psyctext(packet):
text = packet.data
for key, value in packet.vars.items():
text = text.replace('[%s]' % key, str(value))
return text
class Vars(dict):
def __init__(self, vars, existing = {}):
dict.__init__(self, existing)
self.vars = vars
self.vars.update(existing)
def __setitem__(self, key, value):
dict.__setitem__(self, key, value)
self.vars.__setitem__(key, value)
all_mmpvars = (
'_source', '_source_identification', '_source_location', '_source_relay',
'_target', '_context', '_counter', '_length', '_initialize', '_fragment',
'_encoding', '_amount_fragments', '_list_using_modules',
'_list_require_modules', '_list_understand_modules', '_list_using_encoding',
'_list_require_encoding', '_list_understand_encoding',
'_list_using_protocols', '_list_require_protocols',
'_list_understand_protocols', '_trace', '_tag', '_tag_relay', '_relay')

26
mjacob/lib/packets.py Executable file
View File

@ -0,0 +1,26 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
## packets.py
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# <C> Copyright 2007, Manuel Jacob
from __init__ import Vars
class MMPPacket:
def __init__(self, mmpvars = {}, body = ''):
self.vars = {}
self.mmpvars = Vars(self.vars, mmpvars)
self.body = body
class PSYCPacket(MMPPacket):
def __init__(self, mmpvars = {}, psycvars = {}, mc = None, data = ''):
MMPPacket.__init__(self, mmpvars)
self.psycvars = Vars(self.vars, psycvars)
self.mc = mc
self.data = data

159
mjacob/lib/parse.py Executable file
View File

@ -0,0 +1,159 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
## parse.py
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# <C> Copyright 2007, Manuel Jacob
from packets import MMPPacket, PSYCPacket
def parse_vars(lines):
cvars = {}
pvars = {}
_list = None
for i in lines:
tmp = i.split('\t')
if tmp[0] == ':':
vars[_list].append(tmp[1])
continue
elif tmp[0].startswith(':'):
vars = cvars
elif tmp[0].startswith('='):
vars = pvars
if tmp[0][1:6] == '_list':
_list = tmp[0][1:]
vars[_list] = [tmp[1]]
else:
_list = None
vars[tmp[0][1:]] = tmp[1]
return cvars, pvars
class LineBased:
def __init__(self):
self.__buffer = ''
self.linemode = True
def data(self, data):
self.__buffer += data
while self.linemode and self.__buffer:
try:
line, self.__buffer = self.__buffer.split('\n', 1)
self.line(line)
except ValueError:
return
else:
if not self.linemode:
__buffer = self.__buffer
self.__buffer = ''
self.raw(__buffer)
class MMPParser(LineBased): # callback-based
pvars = {}
def __init__(self):
LineBased.__init__(self)
self.reset()
def reset(self):
self.mode = 'vars'
self.varlines = []
self.cvars = {}
self.body = ''
def line(self, line):
if line == '.':
self.recv()
elif self.mode == 'vars':
if line:
self.varlines.append(line)
else:
self.cvars, pvars = parse_vars(self.varlines)
for key, value in pvars.items():
if value:
self.pvars[key] = value
else:
if key in self.pvars:
del self.pvars[key]
self.mode = 'body'
else:
self.body += line + '\n'
def recv(self):
vars = self.pvars.copy()
vars.update(self.cvars)
if self.body and self.body[-1] == '\n':
self.body = self.body[:-1]
packet = MMPPacket(vars, self.body)
packet.not_to_render = True
self.recv_packet(packet)
self.reset()
def recv_packet(self, packet):
raise NotImplementedError
class PSYCParser:
pvars = {}
def parse(self, packet):
lines = packet.body.split('\n')
_is_text = False
cvars = {}
varlines = []
textlines = []
mc = ''
for i in lines:
if not i:
continue
tmp = i.split('\t')
if _is_text:
textlines.append(i)
elif i[0] in (':', '='):
varlines.append(i)
elif i[0] == '_':
cvars, pvars = parse_vars(varlines)
for key, value in pvars.items():
if value:
self.pvars[key] = value
else:
if key in self.pvars:
del self.pvars[key]
mc = i
_is_text = True
text = '\n'.join(textlines)
vars = self.pvars.copy()
vars.update(cvars)
packet = PSYCPacket(packet.mmpvars, vars, mc, text)
packet.not_to_render = True
return packet
if __name__ == '__main__':
MMPParser().data(':_context\ti\n=_foo\tbar\n\n_message_public\nHello\n.\n')
MMPParser().data(':_context\ti\n\n_message_public\nHello\n.\n')

70
mjacob/lib/render.py Executable file
View File

@ -0,0 +1,70 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
## render.py
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# <C> Copyright 2007, Manuel Jacob
from packets import MMPPacket
def to_vars(cvars = {}, pvars = {}, other = {}):
data = ''
for key, value in cvars.items():
data += ":%s\t%s\n" % (key, value)
for key, value in pvars.items():
data += "=%s\t%s\n" % (key, value)
for key, value in other.items():
data += "%s\t%s\n" % (key, value)
return data
class MMPRenderer:
pvars = {}
new_pvars = {}
def render(self, packet):
data = ''
if packet.mmpvars:
data += to_vars(packet.mmpvars, self.new_pvars) + '\n'
self.new_pvars = {}
data += '%s\n.\n' % packet.body
return data.encode('utf-8')
def set_pvar(self, key, value):
self.new_pvars[key] = value
self.pvars[key] = value
def del_pvar(self, key):
self.new_pvars[key] = ''
del self.pvars[key]
class PSYCRenderer:
pvars = {}
new_pvars = {}
def render(self, packet):
data = ''
if packet.psycvars:
data += to_vars(packet.psycvars, self.new_pvars)
self.new_pvars = {}
if packet.mc:
data += packet.mc + '\n'
if packet.data:
data += packet.data
return MMPPacket(packet.mmpvars, data)
def set_pvar(self, key, value):
self.new_pvars[key] = value
self.pvars[key] = value
def del_pvar(self, key):
self.new_pvars[key] = ''
del self.pvars[key]

95
mjacob/old/MMP/MMPProtocol.py Executable file
View File

@ -0,0 +1,95 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# <C> Copyright 2007, Manuel Jacob
from twisted.protocols import basic
def convert_lines(lines):
"""convert list of lines to vars, method (mc) and text"""
_is_text = False
dict = {}
mc = None
textlines = []
for i in lines:
tmp = i.split('\t')
if _is_text:
textlines.append(i)
elif tmp[0] == ':': # second or later item in the list
dict[_list].append(tmp[1])
elif i[0] in (':', '='):
if tmp[0][1:6] == '_list':
_list = tmp[0][1:]
dict[_list] = [tmp[1]]
else: dict[tmp[0][1:]] = tmp[1]
elif i[0] == '_':
mc = i
_is_text = True
text = '\n'.join(textlines)
return dict, mc, text
class MMPProtocol(basic.LineReceiver):
def __init__(self, callback):
self.msg = callback
self.delimiter = '\n'
self.initialized = False
self.charset = 'UTF-8'
self.reset()
def reset(self):
self.lines = []
self._reset()
def _reset(self):
raise NotImplementedError('Implement in Subclass.')
def lineReceived(self, line):
if not self.initialized:
assert line == '.', 'first line is not ".", line is "%s"' % line
self.initialized = True
return
if line == '.': # packet ends
self.recv_packet()
else:
self.lines.append(line)
def recv_packet(self):
h2b = self.lines.index('') # head to body delimiter
mmp = self.lines[:h2b]
data = self.lines[h2b+1:]
mmpvars = convert_lines(mmp)[0]
if '_length' in mmpvars:
bodylength = mmpvars['_length']
else:
bodylength = len(data)
if len(data) == bodylength:
self._recv_packet(mmpvars, data)
self.reset()
def _recv_packet(self, mmpvars, data):
raise NotImplementedError('Implement in Subclass.')
def _send_packet(self, mmpvars, data):
mmpstring = ''
if mmpvars:
for key, value in mmpvars.items():
mmpstring += "=%s\t%s\n" % (key, value)
if not data:
data = ''
send = "%s%s.\n" % (mmpstring, data)
#print ('send', send.encode(self.charset))
self.transport.write(send.encode(self.charset))

View File

14
mjacob/old/PSYC/PSYCPacket.py Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class PSYCPacket:
def __init__(self, mmpvars = {}, psycvars = {}, mc = None, text = ''):
self.mmpvars = mmpvars
self.psycvars = psycvars
self.vars = mmpvars.copy()
self.vars.update(psycvars)
self.mc = mc
self.text = text
def __repr__(self):
return 'vars: %s, mc: %s, text: %s' % (self.vars, self.mc, self.text)

98
mjacob/old/PSYC/PSYCProtocol.py Executable file
View File

@ -0,0 +1,98 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# <C> Copyright 2007, Manuel Jacob
from MMP.MMPProtocol import MMPProtocol, convert_lines
from PSYC.PSYCPacket import PSYCPacket
class PSYCProtocol(MMPProtocol):
mmpvars = (
'_source', '_source_identification', '_source_location', '_source_relay',
'_target', '_context', '_counter', '_length', '_initialize', '_fragment',
'_encoding', '_amount_fragments', '_list_using_modules',
'_list_require_modules', '_list_understand_modules', '_list_using_encoding',
'_list_require_encoding', '_list_understand_encoding',
'_list_using_protocols', '_list_require_protocols',
'_list_understand_protocols', '_trace', '_tag', '_tag_relay', '_relay')
def _reset(self):
pass
def __init__(self, callback):
MMPProtocol.__init__(self, callback)
def _recv_packet(self, mmpvars, data):
#print 'recv'
psycvars, mc, text = convert_lines(data)
packet = PSYCPacket(mmpvars, psycvars, mc, text)
func = getattr(self, "handle_%s" % packet.mc[1:], None)
not_call_msg = False
if func:
not_call_msg = func(packet)
if not not_call_msg:
self.msg(packet)
def handle_status_circuit(self, packet):
self._send_packet(None, None) # send an empty mmp packet (single dot)
self.send_packet(PSYCPacket(mmpvars =
{'_target': self.factory.config.uni}, mc = '_request_link'))
self.charset = packet.vars['_using_characters']
def send_packet(self, packet):
self.factory.ui.outpacketcount += 1
self.factory.ui.pre_send(packet)
data = ''
if packet.psycvars:
data += '\n'
for key, value in packet.psycvars.items():
data += ":%s\t%s\n" % (key, value)
if packet.mc:
data += packet.mc + '\n'
if packet.text:
data += packet.text + '\n'
self._send_packet(packet.mmpvars, data)
################################
# implementation of the PSYC API
################################
def msg(self, packet):
raise NotImplementedError
def sendmsg(self, target, mc, data, vars):
mmpvars = {'_target': target}
psycvars = {}
for key, value in vars.items():
if i in self.mmpvars:
mmpvars[key] = value
else:
psycvars[key] = value
packet = PSYCPacket(mmpvars, psycvars, mc, data)
self.send_packet(packet)
def castmsg(self, source, mc, data, vars):
mmpvars = {'_context': source}
psycvars = {}
for key, value in vars.items():
if i in self.mmpvars:
mmpvars[key] = value
else:
psycvars[key] = value
packet = PSYCPacket(mmpvars, psycvars, mc, data)
self.send_packet(packet)

View File

View File

107
mjacob/old/UI/base.py Executable file
View File

@ -0,0 +1,107 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# <C> Copyright 2007, Manuel Jacob
import re
from twisted.internet import reactor
from PSYC.PSYCPacket import PSYCPacket
re_var = re.compile('\[_.[^\[]+\]')
class BaseUI:
ignored_mcs = []
ignored_users = []
mc_prefixes = ('_notice', '_error', '_info', '_status')
inpacketcount = 0
outpacketcount = 0
connected = False
def recv(self, packet):
self.inpacketcount += 1
if packet.mc in self.ignored_mcs:
return
#if packet.vars['_source'] in self.ignored_users:
# return
self.pre_print(packet)
self._insert_vars(packet)
self.print_(packet)
def _insert_vars(self, packet):
while True:
x = re_var.search(packet.text)
if not x:
break
tmp = x.group()
if tmp[1:-1] in packet.vars:
packet.text = packet.text.replace(tmp, packet.vars[tmp[1:-1]])
def print_(self, packet):
func = None
for i in self.mc_prefixes:
if packet.mc.startswith(i):
func = getattr(self, 'handle_%s_' % i[1:], None)
tmp = getattr(self, 'handle_%s' % packet.mc[1:], None)
if tmp:
func = tmp
if func:
func(packet)
else:
self._print('Server', 'unhandled packetmc (Packet %s)'
% self.inpacketcount)
def handle_notice_(self, packet):
self._print('Server', "%s: %s" % (packet.mc, packet.text))
def handle_error_(self, packet):
self._print('Server', "%s: %s" % (packet.mc, packet.text))
def handle_info_(self, packet):
self._print('Server', "%s: %s" % (packet.mc, packet.text))
def handle_status_(self, packet):
self._print('Server', "%s: %s" % (packet.mc, packet.text))
def handle_message_public(self, packet):
self._print(packet.vars['_context'], '%s says: %s'
% (packet.vars['_nick'], packet.text))
def handle_status_place_topic_official(self, packet):
pass
def handle_echo_logoff(self, packet):
reactor.stop()
def server_command(self, command, target):
if target == 'Server':
target = 'psyc://%s/' % self.factory.config.host
if command.startswith('/go'):
self.factory.sc.send_packet(PSYCPacket(mmpvars = {'_target': command[4:]}, psycvars = {'_nick': self.factory.config.username}, mc = '_request_enter'))
elif command.startswith('/join'):
self.factory.sc.send_packet(PSYCPacket(mmpvars = {'_target': command[6:]}, psycvars = {'_nick': self.factory.config.username}, mc = '_request_enter'))
elif command.startswith('/quit'):
self.factory.sc.send_packet(PSYCPacket(mmpvars = {'_target': self.factory.config.uni, '_source_identification': self.factory.config.uni}, mc = '_request_execute', text = 'bye'))
else:
self.factory.sc.send_packet(PSYCPacket(mmpvars = {'_target': self.factory.config.uni, '_source_identification': self.factory.config.uni}, mc = '_request_input', text = command))
#if command.startswith('/quit'):
# reactor.stop()
#if command.startwith('/part'):
# self.factory.sc.send_packet(PSYCPacket())

18
mjacob/old/UI/text/Interface.py Executable file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# <C> Copyright 2007, Manuel Jacob
from UI import base
class Interface(base.BaseUI):
def __init__(self, factory):
self.factory = factory
self.factory.connect()
#BaseUI.__init__(self)
def _print(self, window, text):
print '%s: %s' % (window, text)

View File

366
mjacob/old/UI/wx/Interface.py Executable file
View File

@ -0,0 +1,366 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# <C> Copyright 2007, Manuel Jacob
import wx
from twisted.internet import reactor
from UI import base
from PSYC.PSYCPacket import PSYCPacket
class Tab(wx.Panel):
def __init__(self, parent, name):
self.name = name
wx.Panel.__init__(self, parent, -1)
self.textctrl = wx.TextCtrl(self, -1, style = wx.TE_MULTILINE|wx.TE_READONLY)
self.users = []
def OnResize(event):
self.textctrl.SetSize(event.GetSize())
self.Bind(wx.EVT_SIZE, OnResize, self)
class MainWindow(wx.Frame):
def __init__(self, ui):
wx.Frame.__init__(self, None, -1, size = (800, 600))
self.Bind(wx.EVT_CLOSE, self.OnClose, self)
self.ui = ui
self.factory = self.ui.factory
self.tabs = {}
splitter = wx.SplitterWindow(self, -1)
self.pl = wx.Panel(splitter, -1)
sizer = wx.GridBagSizer(0, 0)
self.nb = wx.Notebook(self.pl, -1, style = wx.NB_TOP)
sizer.Add(self.nb, (0, 0), (1, 1), wx.EXPAND)
sizer.AddGrowableCol(0)
sizer.AddGrowableRow(0)
self.input = wx.TextCtrl(self.pl, -1)
sizer.Add(self.input, (1, 0), (1, 1), wx.EXPAND)
self.pl.SetSizerAndFit(sizer)
self.add_tab('Server')
self.pr = wx.Panel(splitter, -1) # right panel
self.userlist = wx.ListBox(self.pr, -1, (0, 5), style = wx.SUNKEN_BORDER)
self.nb.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnChangeTab, self.nb)
def OnResize(event):
size = event.GetSize()
self.userlist.SetSize((size[0], size[1] - 10))
self.pr.Bind(wx.EVT_SIZE, OnResize, self.pr)
splitter.SplitVertically(self.pl, self.pr, self.GetSize()[0] * 0.7)
splitter.SetSashGravity(1)
mainmenu = wx.Menu()
menuitem = mainmenu.Append(-1, '&Connect', 'Connect to the server')
self.Bind(wx.EVT_MENU, self.OnConnect, menuitem)
menuitem = mainmenu.Append(-1, '&Debug window',
'Open or close debug window')
self.Bind(wx.EVT_MENU, self.OnOpenDebugWindow, menuitem)
mainmenu.AppendSeparator()
menuitem = mainmenu.Append(-1, '&Exit', 'Exit pyPSYC')
self.Bind(wx.EVT_MENU, self.OnClose, menuitem)
menubar = wx.MenuBar()
menubar.Append(mainmenu, '&pyPSYC')
self.SetMenuBar(menubar)
self.input.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.Show(True)
def OnKeyDown(self, event):
if event.GetKeyCode() in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER):
pagename = self.nb.GetCurrentPage().name
value = self.input.GetValue()
if value:
if pagename == 'Server' or value.startswith('/'):
self.ui.server_command(value, self.nb.GetCurrentPage().name)
elif '@' in pagename:
self.factory.sc.castmsg(pagename, '_message_public',
value, {})
elif '~' in pagename:
self.factory.sc.sendmsg(pagename, '_message_private',
value, {})
self.input.SetValue('')
event.Skip()
def OnChangeTab(self, event):
self.userlist.Set(self.nb.GetCurrentPage().users)
def OnConnect(self, event):
self.factory.connect()
self.ui.connected = True
def OnOpenDebugWindow(self, event):
# open or close window:
if not self.ui.debugwindow.Show(True): self.ui.debugwindow.Show(False)
def OnClose(self, event):
if self.ui.connected:
self.ui.server_command('/quit', None)
else:
reactor.stop()
def add_tab(self, name):
self.tabs[name] = Tab(self.nb, name)
self.nb.AddPage(self.tabs[name], name)
class DebugWindow(wx.Frame):
def __init__(self, factory):
wx.Frame.__init__(self, parent = None, id = -1,
title = 'pyPSYC debug window', size = (800, 600),
style = wx.DEFAULT_FRAME_STYLE|
wx.NO_FULL_REPAINT_ON_RESIZE)
self.Bind(wx.EVT_CLOSE, self.OnClose, self)
self.tree = wx.TreeCtrl(self, -1, wx.DefaultPosition, (-1,-1),
wx.TR_HIDE_ROOT|wx.TR_HAS_BUTTONS)
self.treeroot = self.tree.AddRoot('Programmer')
def OnClose(self, event):
self.Show(False)
class Interface(base.BaseUI):
def __init__(self, factory):
self.factory = factory
self.gui = wx.PySimpleApp()
self.mainwindow = MainWindow(self)
self.debugwindow = DebugWindow(self)
def _print(self, window, text):
if window not in self.mainwindow.tabs:
self.mainwindow.add_tab(window)
self.mainwindow.tabs[window].textctrl.AppendText(text + '\n')
def pre_print(self, packet):
print ('recv', packet)
tmp = self.debugwindow.tree.AppendItem(self.debugwindow.treeroot,
'Incoming Packet %s'
% self.inpacketcount)
self.debugwindow.tree.AppendItem(tmp, "MMPVars: %s" % packet.mmpvars)
self.debugwindow.tree.AppendItem(tmp, "PSYCVars: %s" % packet.psycvars)
self.debugwindow.tree.AppendItem(tmp, "Method: %s" % packet.mc)
self.debugwindow.tree.AppendItem(tmp, "Text: %s" % packet.text)
def pre_send(self, packet):
print ('send', packet)
tmp = self.debugwindow.tree.AppendItem(self.debugwindow.treeroot,
'Outgoing Packet %s'
% self.outpacketcount)
self.debugwindow.tree.AppendItem(tmp, "MMPVars: %s" % packet.mmpvars)
self.debugwindow.tree.AppendItem(tmp, "PSYCVars: %s" % packet.psycvars)
self.debugwindow.tree.AppendItem(tmp, "Method: %s" % packet.mc)
self.debugwindow.tree.AppendItem(tmp, "Text: %s" % packet.text)
def handle_echo_place_enter_login(self, packet):
self._print(packet.vars['_source_relay'], packet.text)
self.mainwindow.tabs[packet.vars['_source_relay']].users = packet.vars['_list_members']
def handle_echo_place_enter(self, packet):
self._print(packet.vars['_nick_place'], packet.text)
self.mainwindow.tabs[packet.vars['_nick_place']].users = packet.vars['_list_members']
if __name__ == '__main__':
app = wx.PySimpleApp()
MainWindow()
app.MainLoop()
##class Tab(wx.Panel):
## def __init__(self, name, main):
## self.name = name
## self.main = main
## self.ui = self.main.ui
## self.factory = self.ui.factory
## wx.Panel.__init__(self, main.nb, -1)
## self.textbox = wx.TextCtrl(self, -1, style = wx.TE_MULTILINE|
## wx.TE_READONLY,)
## self.input = wx.TextCtrl(self, -1)
## #self.input.SetFocus()
## self.input.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
##
## def OnKeyDown(self, event):
## if event.GetKeyCode() in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER):
## if self.input.GetValue():
## self.factory.sc.castmsg(self.name, '_message_public',
## self.input.GetValue(), {})
## self.input.SetValue('')
##
## event.Skip()
#
#
#class Tab(wx.Panel):
# def __init__(self, name, main):
# self.name = name
# self.main = main
# self.ui = self.main.ui
# if hasattr(self.ui, 'factory'):
# self.factory = self.ui.factory
# wx.Panel.__init__(self, main.nb, -1, size = wx.DefaultSize)
# #wx.Frame.__init__(self, None, -1, size = wx.DefaultSize)
#
# #splitter = wx.SplitterWindow(self, -1)
# #pl = wx.Panel(splitter, -1, size = wx.DefaultSize) # panel left
# ##pls = wx.BoxSizer(wx.VERTICAL)
# #wx.TextCtrl(pl, -1, style = wx.TE_MULTILINE|wx.TE_READONLY, size = wx.DefaultSize)
#
# #pr = wx.Panel(splitter, -1, size = wx.DefaultSize) # panel right
# #wx.ListCtrl(pr, -1, size = wx.DefaultSize)
#
# #splitter.SplitVertically(pl, pr)
#
# splitter = wx.SplitterWindow(self, -1)
# panel1 = wx.Panel(splitter, -1)
# wx.StaticText(panel1, -1,
# "Whether you think that you can, or that you can't, you are usually right."
# "\n\n Henry Ford",
# (100,100), style=wx.ALIGN_CENTRE)
# panel1.SetBackgroundColour(wx.LIGHT_GREY)
# panel2 = wx.Panel(splitter, -1)
# panel2.SetBackgroundColour(wx.WHITE)
# splitter.SplitVertically(panel1, panel2)
# self.Centre()
#
#
#class MainWindow(wx.Frame):
# def __init__(self, ui):
# wx.Frame.__init__(self, parent = None, id = -1, title = 'pyPSYC',
# size = (800, 600),
# style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
#
# self.Bind(wx.EVT_CLOSE, self.OnClose, self)
#
# self.ui = ui
# if hasattr(self.ui, 'factory'):
# self.factory = self.ui.factory
#
# self.CreateStatusBar()
#
# # Set up and create the menu
# mainmenu = wx.Menu()
#
# menuitem = mainmenu.Append(-1, '&Connect', 'Connect to the server')
# self.Bind(wx.EVT_MENU, self.OnConnect, menuitem)
#
# menuitem = mainmenu.Append(-1, '&Debug window',
# 'Open or close debug window')
# self.Bind(wx.EVT_MENU, self.OnOpenDebugWindow, menuitem)
#
# mainmenu.AppendSeparator()
#
# menuitem = mainmenu.Append(-1, '&Exit', 'Exit pyPSYC')
# self.Bind(wx.EVT_MENU, self.OnClose, menuitem)
#
# menubar = wx.MenuBar()
# menubar.Append(mainmenu, '&pyPSYC')
# self.SetMenuBar(menubar)
#
# self.nb = wx.Notebook(self, -1, style=wx.NB_BOTTOM)
#
# self.tabs = {}
#
# self.Show(True)
#
# def OnConnect(self, event):
# self.factory.connect()
#
# def OnOpenDebugWindow(self, event):
# # open or close window:
# if not self.ui.debugwindow.Show(True): self.ui.debugwindow.Show(False)
#
# def OnClose(self, event):
# reactor.stop()
#
#
#class DebugWindow(wx.Frame):
# def __init__(self, factory):
# wx.Frame.__init__(self, parent = None, id = -1,
# title = 'pyPSYC debug window', size = (800, 600),
# style = wx.DEFAULT_FRAME_STYLE|
# wx.NO_FULL_REPAINT_ON_RESIZE)
# self.Bind(wx.EVT_CLOSE, self.OnClose, self)
#
# self.tree = wx.TreeCtrl(self, -1, wx.DefaultPosition, (-1,-1),
# wx.TR_HIDE_ROOT|wx.TR_HAS_BUTTONS)
# self.treeroot = self.tree.AddRoot('Programmer')
#
# def OnClose(self, event):
# self.Show(False)
#
#
#class Interface:#(base.BaseUI):
# def __init__(self, factory):
# self.gui = wx.PySimpleApp()
# if factory:
# self.factory = factory
# reactor.registerWxApp(self.gui)
# self.mainwindow = MainWindow(self)
# self.debugwindow = DebugWindow(self)
# window = 'Server'
# self.mainwindow.tabs[window] = Tab(window, self.mainwindow)
# self.mainwindow.nb.AddPage(self.mainwindow.tabs[window], window)
#
# def _print(self, window, text):
# if window not in self.mainwindow.tabs:
# self.mainwindow.tabs[window] = Tab(window, self.mainwindow)
#
# #def OnOvrSize(event, tab=self.mainwindow.tabs[window]):
# # size = event.GetSize()
# # textboxsize = (size[0], size[1] - 24)
# # inputpos = (0, size[1] - 24)
# # tab.textbox.SetSize(textboxsize)
# # tab.input.SetPosition(inputpos)
# # tab.input.SetSize((size[0], 21))
#
# #wx.EVT_SIZE(self.mainwindow.tabs[window], OnOvrSize)
#
# self.mainwindow.nb.AddPage(self.mainwindow.tabs[window], window)
#
# self.mainwindow.tabs[window].textbox.AppendText(text)
#
#
# def pre_print(self, packet):
# tmp = self.debugwindow.tree.AppendItem(self.debugwindow.treeroot,
# 'Incoming Packet %s'
# % self.inpacketcount)
# self.debugwindow.tree.AppendItem(tmp, "MMPVars: %s" % packet.mmpvars)
# self.debugwindow.tree.AppendItem(tmp, "PSYCVars: %s" % packet.psycvars)
# self.debugwindow.tree.AppendItem(tmp, "Method: %s" % packet.mc)
# self.debugwindow.tree.AppendItem(tmp, "Text: %s" % packet.text)
#
# def pre_send(self, packet):
# tmp = self.debugwindow.tree.AppendItem(self.debugwindow.treeroot,
# 'Outgoing Packet %s'
# % self.outpacketcount)
# self.debugwindow.tree.AppendItem(tmp, "MMPVars: %s" % packet.mmpvars)
# self.debugwindow.tree.AppendItem(tmp, "PSYCVars: %s" % packet.psycvars)
# self.debugwindow.tree.AppendItem(tmp, "Method: %s" % packet.mc)
# self.debugwindow.tree.AppendItem(tmp, "Text: %s" % packet.text)
#
#if __name__ == '__main__':
# interface = Interface(None)
# interface.gui.MainLoop()

View File

36
mjacob/old/__init__.py Executable file
View File

@ -0,0 +1,36 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# <C> Copyright 2007, Manuel Jacob
import re
# the following two functions are from the 'original' pypsyc
# (and slightly modified)
def get_host(uni):
m = re.match("^psyc:\/\/(.+)?\/~(.+)?\/?$", uni)
if m: return m.group(1)
m = re.match("^psyc:\/\/([^\/@]+)\@(.+?)\/?$", uni)
if m: return m.group(2)
m = re.match("^psyc:\/\/(.+)?\/\@(.+)?\/?$", uni)
if m: return m.group(1)
m = re.match("^psyc:\/\/(.+)$", uni)
if m: return m.group(1)
raise "invalid uni"
def get_user(uni):
m = re.match("^psyc:\/\/(.+)?\/~(.+)?\/?$", uni)
if m: return m.group(2)
m = re.match("^psyc:\/\/([^\/@]+)\@(.+?)\/?$", uni)
if m: return m.group(1)
raise "invalid uni"

94
mjacob/old/pypsyc-twisted.py Executable file
View File

@ -0,0 +1,94 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Licensed under the GPLv2 license
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# <C> Copyright 2007, Manuel Jacob
import os
from __init__ import get_host, get_user
from PSYC.PSYCProtocol import PSYCProtocol
from PSYC.PSYCPacket import PSYCPacket
try:
from twisted.internet import wxreactor
wxreactor.install()
except ImportError:
pass
from twisted.internet import reactor, protocol
class Config:
def __init__(self, factory):
self.factory = factory
self.settings_folder = self.get_settings_folder()
self.uni_file = os.path.join(self.settings_folder, 'me')
self.clientconfig_file = os.path.join(self.settings_folder, 'pypsyc', 'config')
self.uni = self.get_uni()
#self.uni = 'psyc://beta.ve.symlynX.com/~pypsyctest'
#self.uni = raw_input('uni?: ')
self.host = get_host(self.uni)
self.username = get_user(self.uni)
self.configfile = self.open_configfile()
self.ui = 'wx'
def get_settings_folder(self):
# resolve settings folder
if os.name == 'posix':
folder = os.path.join(os.environ.get('HOME'), ".psyc")
elif os.name == 'nt':
folder = os.path.join(os.environ.get('APPDATA'), 'psyc')
else:
folder = os.path.join(os.getcwd(), 'psyc')
# create settings folder if necessary
try:
os.mkdir(folder)
except OSError:
pass
return folder
def get_uni(self):
f = file(self.uni_file)
uni = f.read().strip()
f.close()
return uni
def open_configfile(self):
return None
class App(protocol.ClientFactory):
def __init__(self):
self.config = Config(self)
self.protocol = PSYCProtocol
self.connections = {}
#Interface = __import__('UI.%s.Interface' % self.config.ui, [], [], ['Interface'])
exec("from UI.%s.Interface import Interface" % self.config.ui)
self.ui = Interface(self)
self.callback_on_recv = self.ui.recv
def connect(self):
reactor.connectTCP(self.config.host, 4404, self, timeout = 20)
def buildProtocol(self, addr):
p = self.protocol(self.callback_on_recv)
p.factory = self
self.callback_on_recv
self.connections[addr.host] = p
if len(self.connections) == 1:
self.sc = p # sc = server connection
return p
factory = App()
reactor.run()

4
mjacob/old/pypsyc.conf Normal file
View File

@ -0,0 +1,4 @@
[server1]
host = beta.ve.symlynX.com
port = 4404
username = l

62
mjacob/server.py Executable file
View File

@ -0,0 +1,62 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Licensed under the MIT license
# http://opensource.org/licenses/mit-license.php
# <C> Copyright 2007, Manuel Jacob
import os
from base import Config, BaseCenter, MMPCircuit
### TODO: This don't works yet ###
class ToClientCircuit(MMPCircuit):
def __init__(self, center):
MMPCircuit.__init__(self, center)
def init(self):
MMPCircuit.init(self)
#self.
class ServerConfig(Config):
def __init__(self, app):
Config.__init__(self, app)
self.serverconfig_file = os.path.join(self.settings_folder, 'config')
def get_settings_folder(self):
# resolve settings folder
if os.name == 'posix':
folder = os.path.join(os.environ.get('HOME'), ".pypsyced")
elif os.name == 'nt':
folder = os.path.join(os.environ.get('APPDATA'), 'pypsyced')
else:
folder = os.path.join(os.getcwd(), 'pypsyced')
# create settings folder if necessary
try:
os.mkdir(folder)
except OSError:
pass
return folder
def open_configfile(self):
return None
class ServerCenter(BaseCenter):
def __init__(self):
self.config = ServerConfig(self)
def recv_mmp_packet(self, packet, physsource):
if not packet.vars and not packet.body:
return
if physsource.linked_uni == None or packet.mmpvars['_target']:
psyc_packet = physsource.psyc_parser.parse(packet)
self.recv_psyc_packet(psyc_packet, physsource)

BIN
mjacob/server.pyc Normal file

Binary file not shown.

View File

BIN
mjacob/socket_/__init__.pyc Normal file

Binary file not shown.

46
mjacob/socket_/twisted_.py Executable file
View File

@ -0,0 +1,46 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from twisted.internet import protocol
from base import MMPCircuit, PSYCCircuit
from client import ClientCenter
from server import ServerCenter
class CircuitBaseProtocol(protocol.Protocol):
def connectionMade(self):
self.init()
def dataReceived(self, data):
self.mmp_parser.data(data)
def _send(self, data):
print (data,)
self.transport.write(data)
class MMPCircuitProtocol(CircuitBaseProtocol, MMPCircuit): pass
class PSYCCircuitProtocol(CircuitBaseProtocol, PSYCCircuit): pass
class TwistedClientCenter(ClientCenter, protocol.ClientFactory):
def buildProtocol(self, addr):
p = PSYCCircuitProtocol(self)
self.connected(p, addr.host)
return p
def connect(self, host, port = 4404):
self.reactor.connectTCP(host, port, self)
#class TwistedServerCenter(ClientCenter, protocol.ClientFactory):
# def buildProtocol(self, addr):
# p = CircuitProtocol(self)
# self.connected(p, addr.host)
# return p
#
# def connect(self, host, port = 4404):
# self.reactor.connectTCP(host, port, self)

BIN
mjacob/socket_/twisted_.pyc Normal file

Binary file not shown.

74
mjacob/twisted_client.py Executable file
View File

@ -0,0 +1,74 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from lib import psyctext
from lib.packets import MMPPacket, PSYCPacket
from socket_.twisted_ import TwistedClientCenter#, reactor
factory = TwistedClientCenter()
#factory.connect(factory.config.host)
debug = True
import base
base.debug = debug
def install_wx(app): # not tested
#del sys.modules['twisted.internet.reactor']
from twisted.internet import wxreactor
wxreactor.install()
from twisted.internet import reactor
reactor.registerWxApp(app)
def install_tk(app): # not tested
from twisted.internet import tksupport
tksupport.install(app)
def install_gtk(app): # not tested
del sys.modules['twisted.internet.reactor']
from twisted.internet import gtkreactor
gtkreactor.install()
from twisted.internet import reactor
def install_gtk2(app): # not tested
del sys.modules['twisted.internet.reactor']
from twisted.internet import gtk2reactor
gtk2reactor.install()
from twisted.internet import reactor
if debug and __name__ == '__main__': # You can test modules here
from base import Module
class DebugModule(Module):
methods = ['*']
def received(self, packet, physsource):
print """packet from %s::
mmpvars: %s
psycvars: %s
mc: %s
text: %s
""" % (physsource, packet.mmpvars, packet.psycvars, packet.mc, psyctext(packet))
factory.register_module(DebugModule())
class AuthModule(Module):
methods = ['_notice_circuit_established']
def handle_notice_circuit_established(self, packet, physsource):
physsource.send_psyc_packet(PSYCPacket(mmpvars = {'_target': factory.config.uni}, mc = '_request_link'))
physsource.mmp_renderer.set_pvar('_source_identification', factory.config.uni)
factory.register_module(AuthModule())
if __name__ == '__main__':
from UI.wx_ import UI
app = UI(factory)
from twisted.internet import reactor
factory.reactor = reactor
print 'start reactor'
reactor.run()

BIN
mjacob/twisted_client.pyc Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
# different user interfaces for pyPSYC

View File

@ -0,0 +1 @@
# different user interfaces for pyPSYC

167
pypsyc/GUI/Qt/Gui.py Normal file
View File

@ -0,0 +1,167 @@
import sys
import qt
from qt import SIGNAL, SLOT
import asyncore
import pypsyc.GUI.Abstract.Gui as AbstractGui
class MainDebugWidget(qt.QWidget):
def __init__(self, parent = None, name = None, fl=0):
qt.QWidget.__init__(self,parent,name,fl)
self.textField = qt.QTextBrowser(self)
self.textField.setGeometry(10, 5, 580, 350)
self.inputField = qt.QMultiLineEdit(self)
self.inputField.setGeometry(10, 360, 580 , 100)
qt.QObject.connect(self.inputField, SIGNAL('returnPressed()'),
self.inputText)
self.resize(qt.QSize(600,480).expandedTo(self.minimumSizeHint()))
sys.stdout = self
def inputText(self):
if self.inputField.text().findRev('\n.\n') - self.inputField.text().length() == -3:
data = self.inputField.text()
print "packet entered"
self.inputField.setText('')
def write(self, string):
self.textField.append(string)
class RoomWidget(qt.QWidget):
def __init__(self, parent = None, name = None, fl = 0):
qt.QWidget.__init__(self, parent, name, fl)
self.name = name
self.textField = qt.QTextBrowser(self)
self.textField.setGeometry(qt.QRect(10, 35, 470, 350))
self.topicField = qt.QLineEdit(self)
self.topicField.setGeometry(qt.QRect(10, 5, 580, 25))
self.nicklist = qt.QListBox(self)
self.nicklist.setGeometry(qt.QRect(490, 35, 100, 350))
self.inputField = qt.QLineEdit(self)
self.inputField.setGeometry(qt.QRect(10, 390, 470, 20))
qt.QObject.connect(self.inputField, SIGNAL('returnPressed()'),
self.inputText)
qt.QObject.connect(self.topicField, SIGNAL('returnPressed()'),
self.inputTopic)
self.resize(qt.QSize(600,550).expandedTo(self.minimumSizeHint()))
def inputText(self):
print self.name + " input: " + self.inputField.text().stripWhiteSpace().latin1()
self.inputField.setText('')
def inputTopic(self):
print self.name + " topic: " + self.topicField.text().stripWhiteSpace().latin1()
def append(self, text): self.textField.append(text.strip() + '\n')
def set_topic(self, text): self.topicField.setText(text)
def inputNickCurrent(self):
print self.name + " current nick:" + self.nickField.text().stripWhiteSpace().latin1()
from pypsyc.PSYC import parsetext
class RoomGui(qt.QMainWindow, AbstractGui.RoomGui):
"""qt frontend for pyPSYC"""
# better inherit from widget made with designer?
def __init__(self):
qt.QMainWindow.__init__(self)
#AbstractGui.RoomGui.__init__(self) # attribute error?
self.model = None
self.rooms = {}
self.topics = {}
self.setCaption("Qt pyPSYC Frontend")
self.menuBar = qt.QMenuBar(self)
self.workspace = qt.QVBox(self)
self.roombar = qt.QTabWidget(self.workspace)
# only in debug mode
self.roombar.addTab(MainDebugWidget(self.workspace), 'main')
self.workspace.show()
self.setCentralWidget(self.workspace)
self.resize(qt.QSize(600, 500)) # fixed size is bad
fileMenu = [("Exit the program", "Quit", 0, "quit")
]
connectionMenu = [("Link to your UNL", "Connect", 0, "connect")
]
self.menus = [("&File", fileMenu),
("&Connection", connectionMenu)]
self.menuActions = {}
for menu in self.menus:
popupMenu = qt.QPopupMenu(self)
for entry in menu[1]:
if entry:
helptxt, menutxt, accel, callbackName = entry
action = qt.QAction(helptxt, qt.QIconSet(qt.QPixmap("./green.gif")),
menutxt, accel, self)
action.addTo(popupMenu)
self.menuActions[callbackName] = action
callback = getattr(self, callbackName, None)
if callback:
qt.QObject.connect(action, SIGNAL("activated()"),
callback)
else:
popupMenu.insertSeparator()
self.menuBar.insertItem(menu[0], popupMenu)
self.show()
def update(self, mc, mmp, psyc):
context = mmp._query("_context")
if mc.startswith("_notice_place_enter"):
## if _source == ME eigentlich...
if not self.rooms.has_key(context):
self.add_room(context)
self.rooms[context].append(parsetext(mmp, psyc))
# elif mc == "_status_place_topic":
# self.topics[context] = parsetext(mmp, psyc)
elif mc == '_message_public':
self.rooms[context].append(psyc._query("_nick") + ": " +
parsetext(mmp, psyc))
def add_room(self, room):
if not self.rooms.has_key(room):
self.rooms[room] = RoomWidget(self.workspace, room)
short = room[room.rfind("@"):] # kurzform für raum... nicht ideal?
self.roombar.addTab(self.rooms[room], short)
def quit(self):
self.app.quit()
def connect(self, host = ''):
self.model.center.connect(host)
class UserGui(qt.QMainWindow, AbstractGui.UserGui):
pass
class MainGui(qt.QApplication, AbstractGui.MainGui):
def __init__(self, argv, center):
qt.QApplication.__init__(self, argv)
AbstractGui.MainGui.__init__(self, argv)
qt.QObject.connect(self, SIGNAL('lastWindowClosed()'),
self, SLOT('quit()'))
self.timer = qt.QTimer()
qt.QObject.connect(self.timer, SIGNAL("timeout()"),
self.socket_check)
def socket_check(self):
asyncore.poll(timeout=0.0)
def run(self):
self.timer.start(100)
self.exec_loop()

View File

@ -0,0 +1 @@
# different user interfaces for pyPSYC

1
pypsyc/GUI/__init__.py Normal file
View File

@ -0,0 +1 @@
# different user interfaces for pyPSYC

28
pypsyc/MMP/MMPPacket.py Normal file
View File

@ -0,0 +1,28 @@
from pypsyc.MMP import Glyphs
#obsolete?
class MMPPacket:
"""encapsulates a set of MMP state variables and data within a
MMP packet. See http://www.psyc.eu/mmp.html"""
# do subclassing for binary data packets and fragmented packets
delimiter = '.\n' # end of packet delimiter
def generate(self, MMPModifiers, data):
"""Expects a (emtpy) dictionary of MMP Modifiers and
a (empty) string of data. Does nothing with the data itself"""
packet = ""
packet += MMPModifiers.plain()
data = data.strip()
# take a look data the first character of data. If it is no glyph
# we may leave out the newline before data
if data.__len__() and _isModifier(data[0]):
packet += '\n'
else:
# keys may be empty (so we either have an empty packet or we
# set default variables for communication
pass
# append data and EOP delimiter
return packet + data + '\n' + MMPPacket.delimiter

84
pypsyc/MMP/MMPProtocol.py Normal file
View File

@ -0,0 +1,84 @@
import asynchat, asyncore, socket
from pypsyc.MMP import Glyphs, _isModifier
from pypsyc.MMP.MMPState import MMPState
debug=0
class MMPProtocol(asynchat.async_chat):
"""modular message protocol
see http://www.psyc.eu/mmp.html for details."""
def __init__(self, callback, connection):
asynchat.async_chat.__init__(self, connection)
self.set_terminator('\n')
self.buffer = ''
self.mmp = MMPState()
self.state = 'mmp'
self.data = []
self.callback = callback
def handle_connect(self):
# dead code neuerdings, eh?
print "connected... but this is dead code"
def handle_close(self):
s = MMPState()
if self.addr[1] != 4404:
s._set('_source', "psyc://%s:%d/"%self.addr)
else:
s._set('_source', "psyc://%s/"%self.addr[0])
self.packetReceived(s, "_notice_circuit_broken\nYour TCP connection to [_source] was broken by the bull from the china shop\n.\n")
self.close()
def collect_incoming_data(self, data):
self.buffer += data
def found_terminator(self):
# hierfuer siehe asyncchat.py... das setzt
# terminator auf 0 zurueck wenns nen int als terminator
# hat und den findet (line 111)
if self.get_terminator() == 0:
self.set_terminator("\n")
line = self.buffer
self.buffer = ''
self.lineReceived(line)
def lineReceived(self, line):
# kind of state machine
line = line.strip()
if line == ".":
## print "<<EOP>>"
self.packetReceived(self.mmp, self.data)
# reset state, clear temporary variables, etc
self.mmp.reset_state()
self.data = []
self.state = 'mmp'
return
if (line == '' or not _isModifier(line)) and self.state == 'mmp':
# if we have _length for data then
# binary support
if self.mmp._query('_length'):
self.set_terminator(self.mmp._query('_length'))
self.state = "data"
## print "<<" + self.state + ">> " + line ## comment out
if self.state == 'mmp':
# maybe else with the above could also work...
self.mmp.set_state(line)
return
elif line != '':
self.data += [line]
def packetReceived(self, mmp, data):
## print "MMPProtocol::packetReceived"
## print "=>MMP :", mmp.get_state()
## print "=>data:", data
self.callback.packetReceived(mmp, data)
def sendPacket(self, mmp, data):
## print "calling MMPProtocol::sendPacket()"
packet = mmp.plain()
packet += '\n'+ data +'\n.\n'
if debug:
print ">>send>>\n", packet, "<<send<<\n"
self.push(packet)

BIN
pypsyc/MMP/MMPProtocol.pyc Normal file

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More