
 xqtr // 2020              __  _                     __  __              
     ____ ___  __  _______/ /_(_)____   ____  __  __/ /_/ /_  ____  ____ 
    / __ `__ \/ / / / ___/ __/ / ___/  / __ \/ / / / __/ __ \/ __ \/ __ \
   / / / / / / /_/ (__  ) /_/ / /__   / /_/ / /_/ / /_/ / / / /_/ / / / /
  /_/ /_/ /_/\__, /____/\__/_/\___/  / .___/\__, /\__/_/ /_/\____/_/ /_/ 
            /____/                  /_/    /____/      MPY Documentation v1.0
                                                 Dec 2020 for version 1.12A46+

  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -
    INDEX                                                               xindex
  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -  
  
    [] ABOUT  ........................................................  xabout
    
    [] HOW TO USE THE GUIDE  .........................................  xhowus
    
    [] FUNCTIONS by GROUPS  ..........................................  xgroup
        + DATE  ......................................................  xgdate
        + SCREEN  ..................................................  xgscreen
        + INPUT  ....................................................  xginput
        + PROMPT  ..................................................  xgprompt
        + ACCESS  ..................................................  xgaccess
        + MESSAGE BASE  ...............................................  xgmsg
        + FILE BASE  .................................................  xgfile
        + NETWORK  ....................................................  xgnet
        + SYSTEM  .....................................................  xgsys
        + OBJECTS  ....................................................  xgobj
        
    [] FAQ & HOW TO  .................................................  xhowto
        + How to get system variables / mci2str  .....................  xhsysv
        + How to get prompt parameters / mci2str  ....................  xhprmt
        + How to download a file  .....................................  xhget
        
    [] SYNTAX  ......................................................  xsyntax
    
    [] SOURCE CODE & EXAMPLES  ......................................  xsource
        + testpython.mpy  //  input and box demo
        + filelist.mpy  //  file base functions demo
        + msgread.mpy  //  message base functions demo
        + getyesnocancel.mpy  //  another getkey() demo
        + purgeinput.mpy  //  purgeinput(), keypressed() demo
        + savescreen.mpy  //  termsize(), charxy() demo
  
    [] CREDITS  .....................................................  xcredit
  
  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -
    ABOUT                                                               xabout
  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -
  
  This document is some type of a manual guide for the Mystic Python scripting
  language. In no way, replaces the wiki page of mystic [wiki.mysticbbs.com] or
  any other official document of Mystic BBS. Take it as it is, i hope it will 
  help others into making mods/scripts easier and if you can, updated it and
  inform me about fixes etc.
  
            As always the document is under a GPL3 license!!!
            
  If you have suggestions to make or noticed something wrong, send me an email
  at: xqtr@gmx.com or visit Another Droid BBS // andr01d.zapto.org:9999
            
  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -
    HOW to USE it
  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -
            
  The guide is divided in four parts. 
  
  1.. Functions by group  ............................................  xgroup
  2.. FAQ and How to  ................................................  xhowto
  3.. Function Details, Syntax, Usage  ..............................  xsyntax
  4.. Example Source Code  ..........................................  xsource
  
  Each section has its purpose. The How To and Source Code, i think are 
  obvious to understand. The Group section, is there, to locate/find easier, 
  what you are looking for. For example if you want to do something with Dates
  and you don't know the appropriate function, taking a look into the Dates 
  section and then reading the syntax and usage of those functions, it will
  help you find what you are looking for.
  
  Some functions may be, into two or more groups, because of their
  functionality, so you will find them listed in all of the groups they might
  belong, but only once in the Syntax section.
  
  Use the Find function of your text editor to navigate through functions, 
  groups, sections etc. Each section, group, guide has its unique id code. 
  Using that code, you can navigate quickly, finding what you want, cause
  the file is getting large enough.
  
  To find the syntax of functions you can search them like by their name, 
  adding the "def" prefix. 
  
  Example:
  
    Search for "def mci2str" instead of "mci2str" to find the syntax of this
    command.
    
  To locate the source code and examples included, use the names of the 
  filenames. For example "testpython.mpy". 

  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -
    FUNCTIONS by GROUPS                                                 xgroup
  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -
 
 -  -- ---- ---- DATE/TIME Functions ------  ---- - - -   -       -     xgdate
 
  These are functions that will help you convert dates, from DOS timestamp to 
  Unix and vice versa. Of course you can use all of the Python2 functions. The
  included delay() function, is making the import of the time module obsolete,
  so you don't have to import it.
 
  dated2u(date)
  datestr(unix_date,format)
  dateu2d(date)
  delay(milliseconds)
  
 -  -- ---- ---- SCREEN Functions ------  ---- - - -   -       -      xgscreen
 
  Functions that appear things on the screen or help you store screen,
  information. In the Source Code section, you will find functions to save
  and restore a screen.
 
  charxy(x,y)
  flush()
  gotoxy(x,y)
  mci2str(code)
  *pause(visible=False)
  pwrite(string)
  pwriteln(string)
  rwrite(string)
  rwriteln(string)
  showfile(filename, baud, pause, abort, onlynew)
  termsize()
  textattr()
  textcolor(attribute)
  wherex()
  wherey()
  write(string)
  writeln(string)
  *writexy(x,y,attr,string)
 
 -  -- ---- ---- INPUT Functions ------  ---- - - -   -       -        xginput
 
  For examples on how to use the functions, see the testpython.mpy demo, 
  inside the scripts directory. The script is also included in the end of this
  file.
  
  backspace(number, destructive)
  getkey()
  getyn(text, default)
  keypressed()
  onekey(chars,echo)
  purgeinput()
  stuffkey(text)
  
 -  -- ---- ---- PROMPT Functions ------  ---- - - -   -       -      xgprompt
 
  getprompt(number)
  setpinfo(byte, string)
  setprompt(number, text)
 
 -  -- ---- ---- ACCESS Functions ------  ---- - - -   -       -      xgaccess
 
  access(acs_string)
  acsnogroup(acs_string)
 
 -  -- ---- ---- MESSAGE BASE Functions ------  ---- - - -   -       -   xgmsg
 
  For examples on how to use the functions, see the msgread.mpy demo, inside
  the scripts directory. The script is also included in the end of this file.
  
  getmbase(num)
  getmbaseid(ID)
  getmgroup(number)
  getmgroupid(number)
  msg_close(mbase)
  msg_delete(handle)
  msg_found(handle)
  msg_gethdr(handle)
  msg_gettxt(handle)
  msg_next(handle)
  msg_open(filename)
  msg_prev(handle)
  msg_seek(handle,index)
  msg_getlr(msgbase, userID)
  msg_setlr(msgbase, userID, userHandle, lastread)
  msg_stats(msgbase)
  
 -  -- ---- ---- FILE BASE Functions ------  ---- - - -   -       -     xgfile
 
  For examples on how to use the functions, see the filelist.mpy demo, inside
  the scripts directory. The script is also included in the end of this file.
 
  fl_close(handle)
  fl_found(handle)
  fl_getdesc(handle)
  fl_getfile(handle)
  fl_next(handle)
  fl_open(basefn)
  fl_prev(handle)
  fl_seek(handle, filenumber, skip)
  getfbase(number)
  getfbaseid(number)
  getfgroup(number
  getfgroupid(number)
  
 -  -- ---- ---- NETWORK Functions ------  ---- - - -   -       -        xgnet
  
  getnetaddr(number)
 
 -  -- ---- ---- SYSTEM Functions ------  ---- - - -   -       -         xgsys
 
  These are very important functions, the proper use or misuse of them, may 
  make your script to halt. Always use the shutdown() function inside loops!
 
  logerror(string)
  mci2str(code)
  menucmd(command,parameters)
  param_count()
  param_str(index)
  shutdown()
  sysoplog(level, text)
  upuser(seclevel)
 
 -  -- ---- ---- OBJECT Functions ------  ---- - - -   -       -         xgobj
 
  These functions return valuable information, about key components of the
  BBS, sush as file, message bases and user information. All of them, return
  a dictionary that contain the data. In each function, you will find a map
  of the dictionary they return, to know what type of data, each function
  returns.
 
  getcfg()
  getfbase(number)
  getfbaseid(number)
  getfgroup(number)
  getfgroupid(number)
  getmbase(num)
  getmbaseid(ID)
  getmgroup(number)
  getmgroupid(number)
  getnetaddr(number)
  getuserid(ID)
  msg_gethdr(handle)
   
  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -
    HOW TO                                                              xhowto
  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -
  
  How to get system variables  .......................................  xhsysv
  
  Some system variables can be found by using the OBJECT functions (above),
  specially the getcfg(). But there is also another way, that you may find 
  easier or give you system information, that the OBJECT functions don't.
  
  This is possible by using the mci2str() function. If you open the 
  display_codes.txt file (inside the doc folder), you will see many MCI codes
  that you can use, to retrieve system and user information.
  
  Example:
    
    mci2str('LO')  # User's last call date in their selected date format
    mci2str('OS')  # Operating system (Windows, Linux, Raspberry Pi, etc)
    
  
  +++------------------------------------- --  ----  -- - - -   -         -
  
  How to get Prompt parameters  ......................................  xhprmt
  
  Some Prompts, specially those that are used for Base scanning (message and
  file), are using parameters, that return information. In case you are using
  a script to handle that function and want to retrieve that information, you
  can do it again by using the mci2str() function.
  
  Example:
    
    mci2str('&1')  # will retrieve first parameter
    mci2str('&2')  # will retrieve second parameter
    etc.
  
  +++------------------------------------- --  ----  -- - - -   -         -
  
  How to download a file  .............................................  xhget
  
  To download a file, using a script command, is doable via this command:
  
  menucmd('F3',filename)
  
  Where <filename> is the full path and name and file. If the file is not 
  found, the download will not start, otherwise it will prompt you to select
  protocol to use.

  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -
    SYNTAX                                                             xsyntax
  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -
  
def access(acsstring): boolean

  Returns True or False if the user passes (has access) to the supplied 
  <acsstring>:

  Example:

    if access("s20"):
      writeln("User has a security level of 20 or higher!")
    else:
      writeln("User has less than 20 security.")
       
  +++------------------------------------- --  ----  -- - - -   -         -

def acsnogroup(access string): boolean

  Process an access string but returns "true" for all group checks regardless
  of the user's group membership. Returns true or false if user has access. If
  the access string, contains a group check (z1,g1 etc.) it will be ignored.
   
  +++------------------------------------- --  ----  -- - - -   -         -
   
def backspace(number, destructive): none

  This function sends (number) backspaces and if (destructive) is True, then 
  it will also delete the character. If False it will only move the cursor
  backwards.
  
  +++------------------------------------- --  ----  -- - - -   -         -
   
def charxy(x,y): tuple 
  
  Returns the character and attribute of the user's screen at the X/Y 
  coordinate.

  Example:

    ch, attr = charxy(1, 1)
    writeln("attr=" + str(attr))
    writeln("char=" + ch)

    OR

    attr = charxy(1,1)[1]
    ch = charxy(1,1)[0]
    
  See also the savescreen.mpy example in the Source Code section.
  
  +++------------------------------------- --  ----  -- - - -   -         -
          
def dated2u(date): integer

  This converts a DOS crunched date to a Unix timestamp
  
  Example:
  
    http://wiki.mysticbbs.com/doku.php?id=python_examples&s[]=datestr

  +++------------------------------------- --  ----  -- - - -   -         -

def datestr(unix_date,format): string

  Converts a Unix date, to a string representation, according the format
  string you will provide.
  
  Example:
  
    http://wiki.mysticbbs.com/doku.php?id=python_examples&s[]=datestr

    writeln("|09Date: " + datestr(dated2u(fileinfo["date"]),
    "NNN DD YYYY HH:II:SS"))

  +++------------------------------------- --  ----  -- - - -   -         -

def dateu2d(date): integer

  This converts a unix date to a DOS crunched date used in places in Mystic.
  This date format is being replaced in Mystic but it still exists in some
  areas.

  +++------------------------------------- --  ----  -- - - -   -         -

def delay(milliseconds): none

  Causes the program to wait for a specific number of milliseconds.

  +++------------------------------------- --  ----  -- - - -   -         -

def fl_close(handle): none

  Closes an opened filelist. 
  
  An example of all the file handling functions can be found in the 
  filelist.mpy demo. http://wiki.mysticbbs.com/doku.php?id=python_examples

  +++------------------------------------- --  ----  -- - - -   -         -

def fl_found(handle): boolean

  Returns true if a message from the last seek or next or prev function has 
  been found.
  
  An example of all the file handling functions can be found in the 
  filelist.mpy demo. http://wiki.mysticbbs.com/doku.php?id=python_examples

  +++------------------------------------- --  ----  -- - - -   -         -

def fl_getdesc(handle): list

  Returns a list that contains the file description.
  
  An example of all the file handling functions can be found in the 
  filelist.mpy demo. http://wiki.mysticbbs.com/doku.php?id=python_examples
  
  +++------------------------------------- --  ----  -- - - -   -         -

def fl_getfile(handle):

  Returns a dictionary with information about the current file.
  
  Return Values:
  
    ulname : uploader
    lines : 2
    number : 1
    filename : file.ans
    dls : 0
    flags : 0
    date : 1253160522
    total : 6
    size : 2213
  
  An example of all the file handling functions can be found in the 
  filelist.mpy demo. http://wiki.mysticbbs.com/doku.php?id=python_examples

  +++------------------------------------- --  ----  -- - - -   -         -

def fl_next(handle):

  Returns the next available file.
  
  An example of all the file handling functions can be found in the 
  filelist.mpy demo. http://wiki.mysticbbs.com/doku.php?id=python_examples

  +++------------------------------------- --  ----  -- - - -   -         -

def fl_open(basefn):

  Opens a filelist where basefn is the filename for the file base, returning a
  handle to the filelist. If a filelist does not exist one will be created.
  
  An example of all the file handling functions can be found in the 
  filelist.mpy demo. http://wiki.mysticbbs.com/doku.php?id=python_examples

  +++------------------------------------- --  ----  -- - - -   -         -

def fl_prev(handle):

  Returns the previous available file
  
  An example of all the file handling functions can be found in the 
  filelist.mpy demo. http://wiki.mysticbbs.com/doku.php?id=python_examples

  +++------------------------------------- --  ----  -- - - -   -         -

def fl_seek(handle, filenumber, skip):

  Seeks to a file number. This should always be called before cycling through
  file listing.  If Skip is set to true, then Deleted records will not be 
  returned.
  
  An example of all the file handling functions can be found in the 
  filelist.mpy demo. http://wiki.mysticbbs.com/doku.php?id=python_examples

  +++------------------------------------- --  ----  -- - - -   -         -

def flush():

  This forces any data currently in Mystic's outbound buffer to be sent to the
  remote client.  In almost all circumstances Mystic handles this for you so
  this will not be needed.

  +++------------------------------------- --  ----  -- - - -   -         -

def getcfg(): dict

  Returns basic configuration options in a Python dictionary.
  
  Return Values:
  
    domain : mybbs.com
    feedback : Sysop Name Here
    msgs : /home/user/mystic/logs/
    text : /home/user/mystic/themes/default/text/
    sysopname : Sysop Name Here
    fvalidate : s0
    logs : /home/user/mystic/logs/
    chatend : 1
    script : /home/user/mystic/themes/default/scripts/
    menu : /home/user/mystic/themes/default/menus/
    ulbase : 0
    system : /home/user/mystic/
    theme : /home/user/mystic/themes/
    semaphore : /home/user/mystic/semaphore/
    qwkbbsid : MYSTIC
    fdlunval : s255
    fseefail : s255
    bbsname : New Mystic BBS
    deftheme : default
    data : /home/user/mystic/data/
    scriptfb : 
    temp : /home/user/mystic/temp1/
    sysopacs : s255
    fseeunval : s255
    textfb : 
    fdlfail : s255
    fallback : True
    chatstart : 1
    defmenu : prelogin
    
  Example:
    
    tmpdir = getcfg()['temp']

  +++------------------------------------- --  ----  -- - - -   -         -

def getfbase(number): dict

  Returns a dictionary of the file base located at record number <number>.
  
  An example of all the file handling functions can be found in the 
  filelist.mpy demo. http://wiki.mysticbbs.com/doku.php?id=python_examples
  
  Return Values:
  
    dispfile : uploadf
    obj : fbase
    name : Uploads
    created : 1455647532
    hatchacs : 
    ftpacs : 
    listacs : z1
    defscan : 1
    filename : uploads
    ftpname : Uploads
    ulacs : s10z1
    tag : 
    flags : 1
    dlacs : 
    address : 0
    listeacs : 
    path : /home/pi/mystic/files/uploads/
    sysopacs : s255
    id : 1
    template : ansiflst

  +++------------------------------------- --  ----  -- - - -   -         -

def getfbaseid(number): dict
  
  Returns a dictionary of the file base with the ID of <number>
  
  An example of all the file handling functions can be found in the 
  filelist.mpy demo. http://wiki.mysticbbs.com/doku.php?id=python_examples
  
  Return Values:
  
    dispfile : uploadf
    obj : fbase
    name : Uploads
    created : 1455647532
    hatchacs : 
    ftpacs : 
    listacs : z1
    defscan : 1
    filename : uploads
    ftpname : Uploads
    ulacs : s10z1
    tag : 
    flags : 1
    dlacs : 
    address : 0
    listeacs : 
    path : /home/pi/mystic/files/uploads/
    sysopacs : s255
    id : 1
    template : ansiflst
  
  +++------------------------------------- --  ----  -- - - -   -         -

def getfgroup(number): dict
  
  Returns a dictionary of the file group at record number <number>.
  
  An example of all the file handling functions can be found in the 
  filelist.mpy demo. http://wiki.mysticbbs.com/doku.php?id=python_examples
  
  Return Values:
  
    info : 
    short : Default
    name : Default File Group
    acs : 
    flags : False
    id : 1

  +++------------------------------------- --  ----  -- - - -   -         -

def getfgroupid(number): dict

  Returns a dictionary of the file group with the ID of <number>.
  
  An example of all the file handling functions can be found in the 
  filelist.mpy demo. http://wiki.mysticbbs.com/doku.php?id=python_examples
  
  Return Values:
  
    info : 
    short : Default
    name : Default File Group
    acs : 
    flags : False
    id : 1


  +++------------------------------------- --  ----  -- - - -   -         -

def getkey(): tuple

  Returns a tuple, containing the character of the key pressed and a boolean
  var, if the key is an extended one.
  
  Example:
  
    print getkey()  #press h
    ('h',False)
    
    print getkey()  #press Up Arrow Key
    ('H',True)

  +++------------------------------------- --  ----  -- - - -   -         -

def getmbase(num): dict

  Returns a Message Base dictionary of the record physically located at (num).
  This example will print all of the message bases out:

  Example:

    import mystic_bbs as bbs
    count = 0
    while not bbs.shutdown():
      mbase = bbs.getmbase(count)
      if mbase is None:
        break
      bbs.writeln(mbase["name"])
      count = count + 1
    bbs.writeln("List complete. |PA")
  
  Return Values:
    
    origin : New Mystic BBS
    postacs : 
    listacs : %
    colorigin : 9
    coltear : 15
    qwkid : 0
    listtype : 0
    id : 1
    addr : 1
    defnscan : 1
    maxage : 0
    filename : email
    qwkconfid : 0
    flags : 16
    echotag : 
    defqscan : 1
    colquote : 11
    coltext : 14
    address : 1
    path : /home/user/mystic/msgs/
    itemplate : ansimlst
    rtemplate : ansimrd
    obj : mbase
    name : E-mail
    header : msghead
    sysopacs : s255
    created : 0
    readtype : 0
    readacs : 
    newsname : 
    qwkname : Email
    nettype : 0
    listeacs : 
    maxmsgs : 0

  +++------------------------------------- --  ----  -- - - -   -         -

def getmbaseid(ID): dict

  Returns a Message Base dictionary of the Message Base with ID of (ID)
  
  Return Values:
    
    origin : New Mystic BBS
    postacs : 
    listacs : %
    colorigin : 9
    coltear : 15
    qwkid : 0
    listtype : 0
    id : 1
    addr : 1
    defnscan : 1
    maxage : 0
    filename : email
    qwkconfid : 0
    flags : 16
    echotag : 
    defqscan : 1
    colquote : 11
    coltext : 14
    address : 1
    path : /home/user/mystic/msgs/
    itemplate : ansimlst
    rtemplate : ansimrd
    obj : mbase
    name : E-mail
    header : msghead
    sysopacs : s255
    created : 0
    readtype : 0
    readacs : 
    newsname : 
    qwkname : Email
    nettype : 0
    listeacs : 
    maxmsgs : 0


  +++------------------------------------- --  ----  -- - - -   -         -

def getmgroup(number): dict
  
  Returns a dictionary of the message group at record number <number>.
  
  Example:
  
    from mystic_bbs import *
    count = 0

    while not shutdown():
      mgroup = getmgroup(count)
      if mgroup is None:
        break
      writeln(mgroup['name'])
      count = count + 1
    writeln("List complete. |PA")
  
  Return Values:
  
    info : 
    short : Default
    obj : group
    name : Default Message Group
    acs : 
    flags : False
    id : 1

  +++------------------------------------- --  ----  -- - - -   -         -

def getmgroupid(number): dict
  
  Returns a dictionary of the message group with the ID of <number>.
  
  Return Values:
  
    info : 
    short : Default
    obj : group
    name : Default Message Group
    acs : 
    flags : False
    id : 1

  +++------------------------------------- --  ----  -- - - -   -         -

def getnetaddr(number): dict

  This function returns a network address configuration passed as number to
  the function.  The result is a dictionary with the following elements:

    zone, net, node, point, desc, domain, primary, addr

  Primary is a 0 or 1 value, 1 being true.  Addr is a string representation
  of zone, net, node, point.

  +++------------------------------------- --  ----  -- - - -   -         -

def getprompt(number): string

  Will return prompt (number) from user's currently selected theme.  If the 
  prompt has been replaced with a script or display file, Mystic will execute 
  the script or show the file.
  
  Example:
  
    print getprompt(1)
    |CR|12Create an account with this BBS? |11

  +++------------------------------------- --  ----  -- - - -   -         -

def getstr(type,length,max,default):string

  Accepts input from the user and returns the string entered. It will show the
  input at the place the cursor is, so use the gotoxy() function to place it
  where you want. You have to press ENTER to exit the function. If nothing
  entered a None object is returned.
  
  type   :  undocumented
  length :  the visible length of the input field
  max    :  the maximum size of the entered string
  default:  a string to show when the function is called
  
  Example:
  
    gotoxy(10,10)
    bbsname = getstr(1,20,40,getcfg()['bbsname'])

  +++------------------------------------- --  ----  -- - - -   -         -

def getuserid(ID): dict
  
  Returns a user dictionary of the user associated with ID.  If no user is 
  found it will return None:
  
  Example:
  
    import mystic_bbs as bbs

    user = bbs.getuserid(1)

    if not user is None:
      bbs.writeln(user["handle"])
  
  Return Value:

    ip : local
    mgroup : 1
    listtype : 1
    id : 1
    city : 
    menu : 
    mbase : 4
    opt10 : 
    fgroup : 1
    theme : default
    opt9 : 
    opt8 : 
    opt5 : 
    opt4 : 
    opt7 : 
    opt6 : 
    opt1 : asdasd
    email : username@mail.com
    opt3 : 
    opt2 : 
    fbase : 1
    handle : username
    host : local
    emailval : 0
    address : 
    hphone : 
    postal : 
    dphone : 
    info : 
    name : name surname
    level : 255
    gender : M
    readtype : 1
    flags : 130

  +++------------------------------------- --  ----  -- - - -   -         -

def getyn(text, default): boolean

  This presents a Yes/No prompt and returns True if they answer yes, False if
  they answer no. The  second parameter is the default selection (True is yes, 
  False is no):

  Example:
  
    if getyn("Is this an example? ", True):
      writeln("You answered Yes!")
    else:
      writeln("You answered No!")

  +++------------------------------------- --  ----  -- - - -   -         -

def gotoxy(x,y): none
  
  This locates the cursor to the X and Y position if the user has ANSI 
  graphics.  If you supply a 0 for either value it will be replaced by the 
  current cursor position.

  +++------------------------------------- --  ----  -- - - -   -         -

def keypressed(): boolean

  Returns True if the user pressed a key, useful for making things, while
  you wait for user input.
  
  Example:
    
    while not keypressed():
      writeln('Hello World')
    
  it will display the phrase "Hello World" until you press any key. See also
  the purgeinput.mpy demo, below.

  +++------------------------------------- --  ----  -- - - -   -         -

def logerror(string):

  This creates an entry into the global error log of the string passed to it, 
  and also creates an entry in the current node log as well.

  +++------------------------------------- --  ----  -- - - -   -         -

def mci2str(code): string

  This function returns the value of an MCI code, where code is the two digit
  MCI code. Notice that you don't have to use the pipe symbol!
  
  Example:
  
    writeln("User: " + mci2str("UH")) #Returns the user handle
    
    mci2str("|UH") is wrong and will give you no result.

  +++------------------------------------- --  ----  -- - - -   -         -

def menucmd(command,parameters): none

  Executes a Mystic command. This is a very powerful, command and allows you
  to make many things! Read the documentation of the available MCI commands, 
  you can use.
  
  Example:
  
    menucmd('GY','scriptname')
    
  It will execute a python script, with name scriptname.

  ++------------------------------------- --  ----  -- - - -   -         -

def msg_close(mbase):

  Closes the handle of a message base.
  
  A complete example of all message base related functions can be found in
  the msgread.mpy script, inside the scripts directory.

  +++------------------------------------- --  ----  -- - - -   -         -

def msg_delete(handle):

  Deletes the message, which the handle is passed, from the message base.

  A complete example of all message base related functions can be found in
  the msgread.mpy script, inside the scripts directory.

  +++------------------------------------- --  ----  -- - - -   -         -

def msg_found(handle): boolean

  Returns true if it finds a message in a message base. The handle parameter,
  is the handle of the message base, that you got from the msg_open()
  function.

  A complete example of all message base related functions can be found in
  the msgread.mpy script, inside the scripts directory.

  +++------------------------------------- --  ----  -- - - -   -         -
  
def msg_gethdr(handle): dict

  Returns a dictionary, containing all header information for the message.

  A complete example of all message base related functions can be found in
  the msgread.mpy script, inside the scripts directory.
  
  Return Value:
  
    highmsg : 7
    received : False
    from : username
    repto : 0
    deleted : False
    local : False
    repnext : 0
    number : 1
    private : False
    dest : 21:1/111
    to : All
    date : 1603799375
    obj : msghdr
    subj : message subject
    repfirst : 0
    sent : False
    orig : 21:1/101

  +++------------------------------------- --  ----  -- - - -   -         -

def msg_gettxt(handle): list

  Returns a list containing the text message.

  A complete example of all message base related functions can be found in
  the msgread.mpy script, inside the scripts directory.

  +++------------------------------------- --  ----  -- - - -   -         -

def msg_next(handle): boolean

  Returns true if there is another message in the message base.
  
  A complete example of all message base related functions can be found in
  the msgread.mpy script, inside the scripts directory.

  +++------------------------------------- --  ----  -- - - -   -         -

def msg_open(filename): handle
  
  Returns a message base handle, if the message base, with the given 
  filename exists.

  A complete example of all message base related functions can be found in
  the msgread.mpy script, inside the scripts directory.
  
  Example:
  
  msg = msg_open(mbase["path"] + mbase["filename"])

  +++------------------------------------- --  ----  -- - - -   -         -

def msg_prev(handle): object

  Retrieves all information from the message before. If there isn't one, it
  will return None.

  A complete example of all message base related functions can be found in
  the msgread.mpy script, inside the scripts directory.
 
  +++------------------------------------- --  ----  -- - - -   -         -

def msg_seek(handle,index): none

  Retrieves information of message with the given index. Must be used, after
  opening a message base, to initialize the base.
  
  A complete example of all message base related functions can be found in
  the msgread.mpy script, inside the scripts directory.

  +++------------------------------------- --  ----  -- - - -   -         -

def msg_getlr(msgbase, userID): integer

  Returns last read message   
  
  Example:
  
  lastread = msg_getlr(msgbase, user["id"]) 
  
  msgbase is the open message base pointer returned by msg_open
  userID is the user's unique ID found in the "id" field of a user
  lastread is a signed 32-bit integer of the last read message which can
  be passed to msg_seek, for example. 

  +++------------------------------------- --  ----  -- - - -   -         -

def msg_setlr(msgbase, userID, userHandle, lastread)

  Sets the last read pointer of the user.

  Example:
  
  msg_setlr (msgbase, user["id"], user["handle"], lastread)

  The user handle should always be passed (not the real name) as this value
  will be hashed as required when a new last read record needs to be added
  for the user.

  +++------------------------------------- --  ----  -- - - -   -         -

def msg_stats(msgbase): tuple

  This function works by passing the handle of an already opened message
  base (msg_open).  It calculates and returns 4 results: Total messages,
  new messages for the current user, number of messages addressed to the
  user, and the unix date of the last post in the message base.   This
  function may cause problems if used within a message reading loop, so
  msg_seek may need to be called afterwards in that case.  Example:

  Example:
  
  total, new, yours, lastpost = msg_stats(msg)

  +++------------------------------------- --  ----  -- - - -   -         -

def onekey(chars,echo): char

  You can use this function to accept specific keypressed from the user. The
  [chars] parameter is a string, containing all valid key presses. The [echo]
  parameter is of type boolean (True/False) and if it's True it will print 
  the key value/char that was pressed.
  
  If the user pressed a key not contained in the [chars] parameter, nothing
  will happen.

  Example:
  
  char = onekey("IQU", True)

  +++------------------------------------- --  ----  -- - - -   -         -

def param_count(): integer

  Returns the number of parameters passed to the script. param_str(#) returns
  the parameter number where # is the number. Supplying (0) will give you the
  script location and name. Supplying nothing will give you the entire 
  command line. 
  
  Example:

  import mystic_bbs as bbs

  bbs.writeln ("Number of parameters..: " + str(bbs.param_count()))
  bbs.writeln ("Full command line.....: " + bbs.param_str())
  bbs.writeln ("Script name...........: " + bbs.param_str(0))
  bbs.writeln ("|CRParameters (param_str):|CR")

  count = 0

  while count <= bbs.param_count():
    bbs.writeln ("   Param #" + str(count) + ": " + bbs.param_str(count))

    count = count + 1

  bbs.writeln("|CR|PA")
  
  +++------------------------------------- --  ----  -- - - -   -         -

def param_str(index): string
  
  See param_count()
  
  Do not use the sys.argz list, cause MPY has problems with it.

  +++------------------------------------- --  ----  -- - - -   -         -

def purgeinput(): none
  
  This clears out any input currently in Mystic's input buffer. In most cases
  when doing any IO with the user Mystic  will handle this on its own.
  
  See the example source code below at purgeinput.mpy, on how is used.
  
  +++------------------------------------- --  ----  -- - - -   -         -

def pause(visible=False): none

  This is not an actual MPY function, but because it's very often useful, i 
  listed here in case you like it.
  
  Source Code:
  
    def pause(visible=False):
      if visible:
        write('|PA')
      else:
        write('|PN')
  
  +++------------------------------------- --  ----  -- - - -   -         -  

def pwrite(string): none
  
  This function accepts a string parameter and will send the contents of the
  string to the user, processing any pipe color codes properly but ignoring 
  other MCI codes.

  Example:

    rwrite("|12Welcome, |UH")
  
  The above example will print "Welcome, |UH" to the user with a red color. 
  Note that the MCI code containing the user name was not translated.
  
  +++------------------------------------- --  ----  -- - - -   -         -

def pwriteln(string): none
  
  This function accepts a string parameter and will send the contents of the
  string to the user, processing any pipe color codes properly but ignoring
  other MCI codes. A CRLF is sent at the end, moving the cursor to the next
  line.

  Example:

    pwriteln("|12Welcome, |UH")
  
  The above example will print "Welcome, |UH" to the user with a red color. 
  Note that the MCI code containing the user name was not translated.
  
  +++------------------------------------- --  ----  -- - - -   -         -

def rwrite(string): none
  
  Raw write. This function accepts a string parameter and will send the
  contents of the string to the user.  This function does NOT filter
  out color or MCI codes nor does it move to the next line.

  Example:

    rwrite("|12Welcome, |UH")
  
  The above example will print "|12Welcome, <UserName>" to the user.
  Note that the color code was untouched.
  
  +++------------------------------------- --  ----  -- - - -   -         -

def rwriteln(string): none
  
  This function accepts a string parameter and will send the contents
  of the string to the user followed by a carrage return to move the
  cursor to the next line.  This function will NOT decode color codes
  or MCI codes.

  Example:

    rwriteln("|12Welcome, |UH")
  
  The above example will print "|12Welcome, <UserName>" to the
  user and then move the cursor to the next line.
  
  +++------------------------------------- --  ----  -- - - -   -         -

def setpinfo(byte, string): none

  Set the prompt info value for <byte> to the <text> in string. 
  
  Example: 
    
    setpinfo(1, "Hello world!")
    writeln(mci2str('|&1'))
    Hello World!
    
  +++------------------------------------- --  ----  -- - - -   -         -

def setprompt(number, text): none

  This sets a theme prompt to the value of text for the duration of the user's
  session or until they change or reload the theme.  The change is not
  permanent.
  
  +++------------------------------------- --  ----  -- - - -   -         -

def showfile(filename, baud, pause, abort, onlynew)

  This function shows a display file and provides all options specific to
  how it is displayed as parameters and returns True or False depending on
  if a display file was found and shown.

  The filename logic uses the same as the GD menu command (see the Wiki
  documentation for GD for more info) which means it has implied paths and
  extensions and random display files if desired.

  The baud parameter is 0 for full speed or whatever the baud rate at which
  Mystic should display the file.

  The Pause parameter is a True/False value that when True will attempt to
  pause on a full screen.

  The Abort parameter is a True/False value that when True will allow the
  file to be adorted while display using the space bar.

  The OnlyNew parameter is a True/False value that when True will only show
  the file if it has been update since the user's last login to the BBS.
  
  Example:

    showfile("gj-glue1", 9600, False, True, False)
    showfile("c:\mystic\textfiles\mytext.txt", 0, True, True, False)
    
  +++------------------------------------- --  ----  -- - - -   -         -

def shutdown(): boolean

  Checks if the user has dropped connection. If so returns True. You should
  always do this check inside loops, otherwise if the connection is dropped
  and the script is inside a loop, it will be in an infinite loop.
  
  Example:

    import mystic_bbs as bbs

      count = 0

      while not bbs.shutdown():
        mbase = bbs.getmbase(count)

        if mbase is None:
          break

        bbs.writeln(mbase["name"])

        count = count + 1

      bbs.writeln("List complete. |PA")
    
  +++------------------------------------- --  ----  -- - - -   -         -

def stuffkey(text): none
  
  This function adds text into the input buffer as if the user had typed it.
  
  +++------------------------------------- --  ----  -- - - -   -         -

def sysoplog(level, text): none
  
  Logs a line to the node's log using level as the loglevel.  Only if the
  loglevel is set equal to or greater than <level> will it be shown in the log.
  Text is a string of text which will have a datestamp added to it
  automatically.

  Example:
  
    sysoplog(1, "Welcome to my Python script!")
    
  +++------------------------------------- --  ----  -- - - -   -         -

def termsize(): tuple
  
  Returns the x and y size of the user's terminal.

  Example:
  
    x, y = termsize()
    
  See the savescreen.mpy example in the Source Code section.
    
  +++------------------------------------- --  ----  -- - - -   -         -

def textattr(): integer

  Returns the current text color attribute. The number is between 0-255. To 
  calculate the foreground color you get the mod of this number and to get the
  background you divide it by 16.
  
  Example:
    
    fg = textattr() % 16
    bg = textattr() // 16
  
  +++------------------------------------- --  ----  -- - - -   -         -

def textcolor(attribute): none

  Sets the current text color to <attribute> where attribute is calculated as 
  (FG + BG) * 16 so white on a blue background would be 15 + 1 * 16 (or 31).
  
  +++------------------------------------- --  ----  -- - - -   -         -

def upuser(seclevel): none

  Updates the current user's security profile to <seclevel>, which includes 
  access flags, time left, daily limits, etc.  Ex: upuser(50)
  
  +++------------------------------------- --  ----  -- - - -   -         -

def wherex(): integer

  Returns the current X location of the cursor
  
  +++------------------------------------- --  ----  -- - - -   -         -

def wherey(): none
  
  Returns the current X location of the cursor
  
  +++------------------------------------- --  ----  -- - - -   -         -

def write(string): none
  
  This function accepts a string parameter and will send the contents
  of the string to the user.  This function will decode all pipe color
  codes and MCI codes in the process.  Note that this function does not
  move the cursor to the next line.

  Example:

    write("|12Welcome, |UH")
  
  The above example will print "Welcome, <UserName>" in Red to the
  user.
  
  +++------------------------------------- --  ----  -- - - -   -         -

def writeln(string): none
  
  This function accepts a string parameter and will send the contents
  of the string to the user followed by a carrage return to move the
  cursor to the next line.  This function will decode all pipe color
  codes and MCI codes in the process.

  Example:

    writeln("|12Welcome, |UH")
  
  The above example will print "Welcome, <UserName>" in Red to the
  user and then move the cursor to the next line.
  
  +++------------------------------------- --  ----  -- - - -   -         -

def writexy(x,y,attr,string): none

  This is not an actuall Mystic Python function, but it's used very often in 
  MPL and is very useful. To imitate it in MPY, use it like this:
  
  Source Code:
    
    def writexy(x,y,attr,string):
      gotoxy(x,y)
      textcolor(attr)
      write(string)

  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -
    SOURCE CODE & EXAMPLES                                             xsource
  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -

/////////////////////////////////////////////////////////////////////////////
/// msgread.mpy example                                                   ///
/////////////////////////////////////////////////////////////////////////////

#####################################################
# Simple Message Reader Example using Mystic Python #
#####################################################

# Reads messages in the user's current message base
# With a pause prompt and basic navigation

from mystic_bbs import *

# Load the current user and then load their current message base
# but fail if they have not selected a message base

user  = getuser(0)
mbase = getmbaseid(user["mbase"])

if mbase is None:
	writeln ("|CRYou have not selected a message base yet!|CR|CR|PA")
	quit()

# Open the message base then check to make sure its open before reading
# data from it

msg = msg_open(mbase["path"] + mbase["filename"]);

if msg is None:
	quit()

done = False

# Seek to the first message in the base and loop while a message
# is found, calling msg_next after each message so the next one
# will be loaded.  Seek must be called first even if reading
# from the first message.

msg_seek(msg, 0)

while msg_found(msg) and not done and not shutdown():

	# Load the message header information into a dictionary
	# and the message text into a list.  The message header
	# must be loaded before the message text will be accessible

	msghdr  = msg_gethdr(msg)
	msgtext = msg_gettxt(msg)

	# Show the message header, setting a line counter that can
	# be used to pause the screen

	writeln("|16|CL|15Msg#: " + str(msghdr["number"]) + " of " + str(msghdr["highmsg"]))
	writeln("|14From: " + msghdr["from"]);
	writeln("|13  To: " + msghdr["to"]);
	writeln("|11Subj: " + msghdr["subj"]);
	writeln("|09-------------------------------------------------------------------------------|07");

	pausecount = 4

	# loop through each line in the message (list) and pause when
	# we get more than 23 lines

	for line in msgtext:

		# before printing a line check if we need to pause

		if pausecount >= 23:
			pausecount = 1

			write("|14*PAUSE* Continue? (|15Y|14)es, (|15N|14)o, (|15Q|14)uit: |07")

			ch = onekey(chr(13) + 'YNQ', False)

			# after getting input, erase the pause prompt then process the input

			backspace(wherex(), True)

			if ch == 'Q':
				done = True
				break
			elif ch == 'N':
				break

		# increase pause counter and send a line of message text
		# unless it is a kludge line:

		if (line == "") or (line != "" and line[0] != chr(1)):
			pausecount += 1
			writeln(line)
		
	# At end of message, so lets give a prompt if we didn't get a quit
	# from the pause prompt:

	if not done:
		write("|CR|16|09MSG READER: (|11A|09)gain, (|11P|09)revious, (|11ENTER|09) Next, (|11Q|09) to Quit: ")

		ch = onekey(chr(13) + 'APQ', True);

		if ch == 'A':
			# do nothing here so it redisplays the same msg
			pass

		elif ch == 'P':
			if msghdr["number"] != 1:
				msg_prev(msg)

		elif ch == 'Q':
			done = True
			break	
		else:
			msg_next(msg);	

# Close the message base and report that we're done

msg_close(msg)

writeln("|CR|12Program complete: Press a key|PN");

/////////////////////////////////////////////////////////////////////////////
/// filelist.mpy example                                                  ///
/////////////////////////////////////////////////////////////////////////////

###################################################
# Simple File Listing Example using Mystic Python #
###################################################

# List files in the user's current file base

from mystic_bbs	import *

# filelist flags

file_offline = int("01")
file_invalid = int("02")
file_deleted = int("04")
file_failed  = int("08")
file_free    = int("10")
file_hatched = int("20")

# Load the current user and then load their current file base
# but fail if they have not selected a file base

user  = getuser(0)
fbase = getfbaseid(user["fbase"])

if fbase is None:
	writeln ("|CRYou have not selected a file base yet!|CR|CR|PA")
	quit()

# Open the file list then check to make sure its open before reading
# data from it

flist = fl_open(fbase["filename"]);

if flist is None:
	writeln("Cannot open file listing. Press a key|PN")
	quit()

done = False

# Seek to the first file in the list and loop while a file is found calling
# next after each file so the next one will be loaded.  Seek should always be
# called before cycling through the list

fl_seek(flist, 0, True)

while fl_found(flist) and not done and not shutdown():

	# Load the file information into a dictionary and then get the file
	# description
	
	fileinfo = fl_getfile(flist)
	filedesc = fl_getdesc(flist)

	# Show the file

	writeln("|CL|14File #" + str(fileinfo["number"]) + " of " + str(fileinfo["total"]))
	writeln("")
	writeln("|09File: |11" + fileinfo["filename"])
	writeln("|09Date: " + datestr(dated2u(fileinfo["date"]), "NNN DD YYYY HH:II:SS"))
	writeln("Size: " + str(fileinfo["size"]))
	writeln("DLs : " + str(fileinfo["dls"]))
	writeln("ULer: " + fileinfo["ulname"])

	flags = ""
	
	if fileinfo["flags"] & file_offline:
		flags = flags + "OFFLINE "
		
	if fileinfo["flags"] & file_invalid:
		flags = flags + "INVALID "
		
	if fileinfo["flags"] & file_deleted:
		flags = flags + "DELETED "
		
	if fileinfo["flags"] & file_failed:
		flags = flags + "FAILED "
		
	if fileinfo["flags"] & file_free:
		flags = flags + "FREE "
		
	if fileinfo["flags"] & file_hatched:
		flags = flags + "HATCHED "
		
	if flags == "":
		flags = "NONE"
	
	writeln("Flag: |13" + flags)
	writeln("")
	writeln("|14Description (" + str(fileinfo["lines"]) + " lines):")
	writeln("|03")
	
	# only print up to the first 10 lines so we can fit it on the screen
	
	for line in range(min(fileinfo["lines"], 10)):
		writeln(filedesc[line])
		
	writeln("")
	write("|16|09File List: (|11A|09)gain, (|11P|09)revious, (|11ENTER|09) Next, (|11Q|09) to Quit: |14")
	
	ch = onekey(chr(13) + 'APQ', True);

	if ch == 'A':
		# do nothing here so it redisplays the same file
		pass

	elif ch == 'P':
		fl_prev(flist)

	elif ch == 'Q':
		done = True
		break	
	else:
		fl_next(flist);	

# Close the file list and report that we're done

fl_close(flist)

writeln("|CR|12Program complete: Press a key|PN");

/////////////////////////////////////////////////////////////////////////////
/// testpython.mpy example                                                ///
/////////////////////////////////////////////////////////////////////////////


##############################################################
## INITIALIZE BBS FUNCTIONS AND DEFINE ANY GLOBAL VARIABLES ##
##############################################################

from mystic_bbs import *

KEY_UP       = chr(72)       # Some keyboard code defines returned by input functions
KEY_DOWN     = chr(80)
KEY_ESCAPE   = chr(27)
KEY_ENTER    = chr(13)
KEY_TAB      = chr(9)
KEY_LEFT     = chr(75)
KEY_RIGHT    = chr(77)

user_deleted = int("00000004")  # user deleted flag from records.pas

thisuser = getuser(0);    # read the currently logged in user into thisuser


################################
## CUSTOM FUNCTION: USER LIST ##
################################

def show_user_list():

	writeln("|15|16|CL|17 Python Demonstration Program > User Listing|$X79 |16|CR")

	count = 1
	user  = getuser(count)
	shown = 0

	while not user is None:

		if not user["flags"] & user_deleted:

			writeln(user["handle"])
			shown = shown + 1

		count = count + 1
		user  = getuser(count)	

	write("|CR|14Listed |15" + str(shown) + " |14user(s)|CR|CR|PA")



################################
## CUSTOM FUNCTION: BOX DEMO  ##
################################

def do_box_demo():

	box = box_options()

	box["header"]  = " Demo Box "
	box["restore"] = False

	writeln(str(box["attr1"]) + "|PN")

	#box_open  (box, 20, 3, 60, 9)
	#box_close (box)


#################################
## CUSTOM FUNCTION: INPUT DEMO ##
#################################

def do_input_demo():

	writeln("|15|16|CL|17 Python Demonstration Program > Input Demo|$X79 |16")

	write('|CR|09Type a string: ');

	input = getstr(11, 40, 120, "Default");

	writeln("|CR|13Enter characters and press |05[|15ESCAPE|15|05] |13when done:|07|CR")

	while not shutdown():

		char, extended = getkey();

		if extended:

			if char == KEY_UP:
				writeln("you pressed up arrow");
			elif char == KEY_DOWN:
				writeln("you pressed down arrow");
			elif char == KEY_LEFT:
				writeln("you pressed left arrow");
			elif char == KEY_RIGHT:
				writeln("you pressed right arrow");
			else:
				writeln("You pressed extended key #" + str(ord(char)))

		else:
			if char == KEY_ESCAPE:
				writeln("you pressed ESCAPE");
				break;
			elif char == KEY_ENTER:
				writeln("you pressed enter");
			elif char == KEY_TAB:
				writeln("you pressed tab");
			else:
				writeln("you pressed character " + char);


################################
## CUSTOM FUNCTION: MAIN MENU ##
################################

def show_main_menu():

	while not shutdown():

		writeln("|15|16|CL|17 Python Demonstration Program > Main Menu|$X79 |16")

		writeln("|CR|15Hello, |14" + thisuser["handle"] + " #" + str(thisuser["id"]) + "|15!  Please select one of the following options:|CR")
		writeln("  |09(|11I|09) Input Demo")
		writeln("  |09(|11U|09) User List")
		write  ("|CREnter option (|11Q|09/|11Quit|09): |11")

		# get one character input using defined list of valid keys

		char = onekey("IQU", True)

		if char == "I":
			do_input_demo()

		if char == "Q":
			break

		if char ==  "U":
			show_user_list()

		

###################################
## PROGRAM EXECUTION BEGINS HERE ##
###################################

show_main_menu()

writeln("|CR|14Demo complete! |PA");

/////////////////////////////////////////////////////////////////////////////
/// getyesnocancel.mpy                                                    ///
/////////////////////////////////////////////////////////////////////////////

KEY_UP       = chr(72)    
KEY_DOWN     = chr(80)
KEY_ESCAPE   = chr(27)
KEY_ENTER    = chr(13)
KEY_TAB      = chr(9)
KEY_BACKSPACE= chr(8)
KEY_LEFT     = chr(75)
KEY_RIGHT    = chr(77)
KEY_PGUP    = chr(73)
KEY_PGDN    = chr(81)
KEY_END     = chr(79)
KEY_HOME    = chr(71)
KEY_SPACE   = chr(32)

def getync(x,y,trueat,offat,default,yes=" Yes ",no=" No ",cancel=" Cancel "):
    """
    Function to get a Yes/No/Cancel answer
    trueat  : color in byte value for the No button
    falseat : color in byte value for the Yes button
    default : True/False to begin with 
    returns : 0 for Yes, 1 for No, 2 for Cancel
    """
    global exit_code
    exit_code = ""
    key = ""
    val = {1:no, 0:yes, 2:cancel}
    res = default
    while key != KEY_ENTER:
        writexy(x,y,offat,val[0]+val[1]+val[2])
        if res == 1:
            writexy(x+len(val[0]),y,trueat,val[res])
        elif res == 0:
            writexy(x,y,trueat,val[res])
        elif res == 2:
            writexy(x+len(val[1])+len(val[0]),y,trueat,val[res])
        gotoxy(1,25)
        key, extended = getkey()
        if key == KEY_LEFT: 
            res -= 1
            if res < 0:
                res = 2
        elif key == KEY_RIGHT or key == KEY_SPACE:
            res += 1
            if res > 2:
                res = 0
        
    return res


/////////////////////////////////////////////////////////////////////////////
/// purgeinput.mpy                                                        ///
/////////////////////////////////////////////////////////////////////////////

# In this example you can see how the purgeinput() function works and also
# the keypressed(). if you remove the purgeinput() func. below, the script
# will exit with only one keypress, cause the buffer is full and the 
# keypressed() function will not clear it, when used again. So, we use the
# purgeinput() to clear the buffer, so next time we call the keypressed()
# func, it works again.


# coding=utf8

from mystic_bbs import *

seq = [chr(250)+chr(249)+chr(205)+chr(240)+chr(205)+chr(249)+chr(250),
'-'+chr(196)+chr(205)+chr(240)+chr(205)+chr(196)+'-',
chr(197)+chr(206)+chr(197),
'dbpqq',
'0123456789876543210',
chr(221)+chr(219)+chr(222)+chr(219)+chr(221),
chr(176)+chr(177)+chr(178)+chr(219)+chr(178)+chr(177)+chr(176),
'-\|/-',
'.:;|'+chr(179)+';:.',
chr(250)+chr(249)+chr(254)+chr(219)+chr(254)+chr(249)+chr(250),
chr(220)+chr(221)+chr(223)+chr(222)+chr(220),
chr(191)+chr(217)+chr(192)+chr(218)+chr(191),
chr(250)+chr(249)+chr(43)+chr(197)+chr(206)+chr(197)+chr(43)+chr(249)+chr(250),
chr(45)+chr(61)+chr(240)+chr(61)+chr(45),
'.oO080Oo.'
]

def anim(x,y,chars):
  i = 0
  d = True
  while not keypressed() and not shutdown():
    gotoxy(x,y)
    write(chars[i])
    gotoxy(1,25)
    delay(200)
    i += 1
    if i == len(chars)-1:
      i = 0

anim(10,10,seq[0])
purgeinput()
anim(10,10,seq[2])
anim(10,10,seq[3])
anim(10,10,seq[5])


/////////////////////////////////////////////////////////////////////////////
/// savescreen.mpy                                                        ///
/////////////////////////////////////////////////////////////////////////////

  These are three very helpful functions to use. With them you can save a 
  screen and also restore it, by using the charxy() functions. Also, the
  functions will work in all terminal sizes, because they use the termsize()
  function to determine the size of it.
  

def savescreen():
  w,h = termsize()
  scr = []
  for y in range(h):
    for x in range(w):
      scr.append(charxy(x,y))
  return scr
  
  
def getscreenxy(scr,x,y):
  w,h = termsize()
  return scr[x+y*w]
  
def restorescreen(scr):
  w,h = termsize()
  for y in range(h):
    for x in range(w):
      c,a = scr[x+y*w]
      if ord(c)<>0:
        write(c + str(a))
        
screen = savescreen()
writeln(' blah blah blah')
restorescreen(screen)        


  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -
    CREDITS                                                            xcredit
  -    --    - - -    ------  ----------------- ---------- --- --- -- - -    -

  How i couldn't give credit to g00r00, the man behind Mystic and all these!
  A big thank you to him, for his efforts in making and supporting Mystic BBS.
  
  This document is a way to say thank you to him, support the Mystic community
  and give a "push" to others to do so, cause Mystic needs some help in making
  a good documentation, that is up to date and not only mods.
  

  +++------------------------------------- --  ----  -- - - -   -         -

         _____         _   _              ____          _   _ 
        |  _  |___ ___| |_| |_ ___ ___   |    \ ___ ___|_|_| |        8888
        |     |   | . |  _|   | -_|  _|  |  |  |  _| . | | . |     8 888888 8
        |__|__|_|_|___|_| |_|_|___|_|    |____/|_| |___|_|___|     8888888888
                                                                   8888888888
                DoNt Be aNoTHeR DrOiD fOR tHe SySteM               88 8888 88
                                                                   8888888888
 /: HaM RaDiO   /: ANSi ARt!     /: MySTiC MoDS   /: DooRS         '88||||88'
 /: NeWS        /: WeATheR       /: FiLEs         /: SPooKNet       ''8888"'
 /: GaMeS       /: TeXtFiLeS     /: PrEPardNeSS   /: FsxNet            88
 /: TuTors      /: bOOkS/PdFs    /: SuRVaViLiSM   /:            8 8 88888888888
                                                              888 8888][][][888
   TeLNeT : andr01d.zapto.org:9999 / ssh: 8888                  8 888888##88888
   SySoP  : xqtr                   eMAiL: xqtr@gmx.com          8 8888.####.888
   DoNaTe : https://paypal.me/xqtr                              8 8888##88##888
  
