mirror of git://git.psyced.org/git/pypsyc
last state we had in cvs
This commit is contained in:
commit
0f02e9cd76
|
@ -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)
|
|
@ -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?
|
||||
|
|
@ -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
|
|
@ -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 |
|
||||
| |
|
||||
---------------------------------
|
|
@ -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
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
# different user interfaces for pyPSYC
|
Binary file not shown.
|
@ -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()
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
# different user interfaces for pyPSYC
|
|
@ -0,0 +1 @@
|
|||
# different user interfaces for pyPSYC
|
Binary file not shown.
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
# different user interfaces for pyPSYC
|
Binary file not shown.
|
@ -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()
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 78 KiB |
|
@ -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()
|
|
@ -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
|
||||
|
|
@ -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.
|
|
@ -0,0 +1,4 @@
|
|||
# toplevel makefile
|
||||
clean :
|
||||
find . -name "*~" | xargs rm
|
||||
find . -name "*.pyc" | xargs rm
|
|
@ -0,0 +1,6 @@
|
|||
Obtain the newest version by git...
|
||||
|
||||
git clone git://git.psyced.org/pypsyc
|
||||
|
||||
See also http://about.psyc.eu/pypsyc
|
||||
|
|
@ -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 ;]]
|
||||
|
|
@ -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
|
|
@ -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()
|
||||
|
|
@ -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>
|
||||
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
@ -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)
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
onesided zlib compression
|
|
@ -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)
|
|
@ -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)
|
|
@ -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"]
|
|
@ -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
|
|
@ -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'])
|
|
@ -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'
|
|
@ -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"""
|
|
@ -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)
|
|
@ -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)
|
|
@ -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()
|
|
@ -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()
|
|
@ -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
|
|
@ -0,0 +1,6 @@
|
|||
[main]
|
||||
uni: psyc://your.host/~yournick
|
||||
password: yourpass
|
||||
|
||||
[interface]
|
||||
splashscreen: pix/symlynX.gif
|
|
@ -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)
|
||||
|
||||
|
|
@ -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)
|
|
@ -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()
|
|
@ -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()
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -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()
|
|
@ -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"""
|
||||
|
|
@ -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)
|
|
@ -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)
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from gui.psycObjects import wxCenter
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
center = wxCenter()
|
||||
center.run()
|
||||
#center = wx.wxCenter()
|
||||
#center.heartbeat()
|
|
@ -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...
|
Binary file not shown.
|
@ -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)
|
Binary file not shown.
|
@ -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)
|
Binary file not shown.
|
@ -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))
|
Binary file not shown.
|
@ -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')
|
|
@ -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
|
|
@ -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')
|
|
@ -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]
|
|
@ -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))
|
|
@ -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)
|
|
@ -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)
|
|
@ -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())
|
|
@ -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)
|
|
@ -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()
|
|
@ -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"
|
|
@ -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()
|
|
@ -0,0 +1,4 @@
|
|||
[server1]
|
||||
host = beta.ve.symlynX.com
|
||||
port = 4404
|
||||
username = l
|
|
@ -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)
|
Binary file not shown.
Binary file not shown.
|
@ -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)
|
Binary file not shown.
|
@ -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()
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
# different user interfaces for pyPSYC
|
|
@ -0,0 +1 @@
|
|||
# different user interfaces for pyPSYC
|
|
@ -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()
|
|
@ -0,0 +1 @@
|
|||
# different user interfaces for pyPSYC
|
|
@ -0,0 +1 @@
|
|||
# different user interfaces for pyPSYC
|
|
@ -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
|
|
@ -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)
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue