Maximus BBS

Documentation for Maximus BBS — Next Generation

View on GitHub

User & Session

User record access, privilege checks, time management, class lookups, user file operations, and caller log

The caller is the center of everything your BBS does. Who are they? What privilege level do they have? How long have they been on? When does their subscription expire? MEX gives you full read access to the user record through the usr global, and a set of functions for managing time, checking privileges, searching the user file, and reading the caller log.

The usr struct itself is documented on the Variables & Types page with every field listed. This page covers the functions that operate on users and sessions — the things that go beyond just reading fields.


On This Page


Quick Reference

Time

Function What It Does
time() Current time as seconds since epoch
timeon() Seconds the caller has been online this session
timeleft() Seconds remaining in the caller’s session
timeadjust() Add or subtract time from the caller’s session
timeadjustsoft() Soft time adjustment (doesn’t exceed daily limit)
time_check() Enable or disable time-limit checking
timestamp() Get current date/time as a _stamp struct
stamp_string() Convert a _stamp to a human-readable string
stamp_to_long() Convert a _stamp to a long for comparison
long_to_stamp() Convert a long back to a _stamp

Privilege & Class

Function What It Does
privok() Check if caller meets a privilege string
class_info() Query class limits (time, downloads, etc.)
class_abbrev() Get the short name for a privilege level
class_name() Get the full name for a privilege level
class_loginfile() Get the login display file for a privilege level
class_to_priv() Convert a class name string to a privilege number

User File

Function What It Does
userfindopen() Start searching the user file
userfindnext() Get the next matching user
userfindprev() Get the previous matching user
userfindclose() Close the user file search
userfindseek() Seek to a specific user record number
userfilesize() Get total number of user records
userupdate() Update a user record
usercreate() Create a new user record
userremove() Delete a user record

Time Management

time()

Returns the current time as an unsigned long — seconds since the Unix epoch. Useful for seeding random numbers, computing elapsed time, and comparing timestamps:

unsigned long time();
unsigned long: start;
start := time();
// ... do work ...
print("That took " + ultostr(time() - start) + " seconds.\n");

timeon()

Seconds the caller has been online this session:

unsigned long timeon();

timeleft()

Seconds remaining before the caller’s time limit expires:

unsigned long timeleft();
if (timeleft() < 300)
  print("You have less than 5 minutes left.\n");

timeadjust(delta)

Add or subtract seconds from the caller’s remaining time. Positive values add time, negative values subtract. Returns the new time remaining:

long timeadjust(long: delta);
// Award 5 minutes for completing a survey
timeadjust(300);
print("Bonus! 5 minutes added to your session.\n");

timeadjustsoft(delta)

Like timeadjust(), but won’t let the caller exceed their daily class limit. Use this when you want to be generous without breaking the rules:

long timeadjustsoft(long: delta);

time_check(state)

Enable or disable automatic time-limit enforcement. Pass 0 to disable, 1 to enable. Returns the previous state:

int time_check(int: state);
// Disable time checking during a game
int: old;
old := time_check(0);
play_game();
time_check(old);

timestamp(stamp)

Fill a _stamp struct with the current date and time:

void timestamp(ref struct _stamp: stamp);
struct _stamp: now;
timestamp(now);
print("Current hour: " + itostr(now.time.hh) + "\n");

stamp_string(stamp)

Convert a _stamp to a human-readable date/time string:

string stamp_string(ref struct _stamp: stamp);
print("Last call: " + stamp_string(usr.ludate) + "\n");

stamp_to_long(stamp)

Convert a _stamp to an unsigned long for numeric comparison:

unsigned long stamp_to_long(ref struct _stamp: st);
// Check if cached data is stale (more than 3600 seconds old)
unsigned long: cached_time, now_time;
cached_time := stamp_to_long(cached_stamp);
struct _stamp: now;
timestamp(now);
now_time := stamp_to_long(now);
if (now_time - cached_time > 3600)
  print("Cache is stale.\n");

long_to_stamp(time, stamp)

Convert a long back to a _stamp:

void long_to_stamp(long: time, ref struct _stamp: st);

Privilege & Class

privok(privstr)

Check if the current caller meets a privilege requirement. The privstr is a privilege expression (e.g., "Sysop", "Normal/K1"):

int privok(string: privstr);
if (privok("Sysop"))
  print("You have sysop access.\n");

class_info(priv, CIT)

Query the limits and settings for a privilege class:

long class_info(int: priv, int: CIT);
Query Constant Returns
CIT_NUMCLASSES Total number of defined classes (pass -1 for priv)
CIT_DAY_TIME Daily time limit (minutes)
CIT_CALL_TIME Per-call time limit (minutes)
CIT_DL_LIMIT Download limit (KB per day)
CIT_RATIO Upload/download ratio
CIT_MAX_CALLS Maximum calls per day
CIT_LEVEL Numeric privilege level
CIT_CLASSKEY Class key
CIT_ACCESSFLAGS Access flags (CFLAGA_* constants)
CIT_MAILFLAGS Mail flags (CFLAGM_* constants)
long: daily_limit;
daily_limit := class_info(usr.priv, CIT_DAY_TIME);
print("Your daily time limit: " + ltostr(daily_limit) + " minutes.\n");

To query by class index instead of privilege level, OR the query type with CIT_BYINDEX:

// Get name of class at index 0
long: num_classes;
num_classes := class_info(-1, CIT_NUMCLASSES);

class_abbrev(priv)

Returns the short abbreviation for a privilege level:

string class_abbrev(int: priv);

class_name(priv)

Returns the full name for a privilege level:

string class_name(int: priv);
print("Your access level: " + class_name(usr.priv) + "\n");

class_loginfile(priv)

Returns the path to the login display file for a privilege level:

string class_loginfile(int: priv);

class_to_priv(s)

Convert a class name string (e.g., "Sysop") to its numeric privilege value:

unsigned int class_to_priv(string: s);

User File Operations

These functions let you search, read, and modify the user database. They work with a _usr struct that you provide — not the global usr.

userfindopen(name, alias, u)

Start a user search. Pass the name and/or alias to search for (empty string to match any). Returns 0 on success, non-zero if no match:

int userfindopen(string: name, string: alias, ref struct _usr: u);
struct _usr: found;
if (userfindopen("John Smith", "", found) = 0)
  print("Found: " + found.name + " from " + found.city + "\n");
userfindclose();

userfindnext(u)

Get the next matching user. Returns 0 on success:

int userfindnext(ref struct _usr: u);

userfindprev(u)

Get the previous matching user:

int userfindprev(ref struct _usr: u);

userfindclose()

Close the user search. Always call this when done:

void userfindclose();

userfindseek(rec, u)

Jump to a specific user record by record number:

int userfindseek(long: rec, ref struct _usr: u);

userfilesize()

Returns the total number of records in the user file:

long userfilesize();

userupdate(u, origname, origalias)

Update an existing user record. Pass the original name and alias so Maximus can find the correct record to update:

int userupdate(ref struct _usr: u, string: origname, string: origalias);

usercreate(u)

Create a new user record:

int usercreate(ref struct _usr: u);

userremove(u)

Delete a user record:

int userremove(ref struct _usr: u);

User List Example

struct _usr: u;
char: nonstop;

nonstop := False;
reset_more(nonstop);

if (userfindopen("", "", u) = 0)
{
  do
  {
    print(strpad(u.name, 25, ' ') + " " + u.city + "\n");
    if (do_more(nonstop, COL_CYAN) = 0)
      goto done;
  }
  while (userfindnext(u) = 0);
}

done:
userfindclose();

Caller Log

Read the historical caller log (who called, when, what they did):

int call_open();
void call_close();
long call_numrecs();
int call_read(long: recno, ref struct _callinfo: ci);

The _callinfo struct contains:

Field Type Description
ci.name string Caller’s name
ci.city string Caller’s city
ci.login struct _stamp Login time
ci.logoff struct _stamp Logoff time
ci.task int Node number
ci.flags unsigned int Call flags (CALL_* constants)
ci.calls unsigned int Total calls at time of login
ci.read unsigned int Messages read during call
ci.posted unsigned int Messages posted during call
struct _callinfo: ci;
long: total, i;

if (call_open() = 0)
{
  total := call_numrecs();
  // Show last 5 callers
  for (i := total - 5; i < total; i := i + 1)
  {
    if (i >= 0 and call_read(i, ci) = 0)
      print(strpad(ci.name, 20, ' ') + " " + stamp_string(ci.login) + "\n");
  }
  call_close();
}

Session Info

The id global (struct _instancedata) provides session-level information:

Field Type Description
id.task_num unsigned int Node/task number
id.local int Non-zero if caller is local
id.port int COM port number
id.speed unsigned long Connection speed (baud rate)
id.alias_system int Non-zero if this is an alias system
id.ask_name int Non-zero if system asks for real name
if (id.local)
  print("You're logging in locally.\n");
else
  print("Connected at " + ultostr(id.speed) + " baud.\n");

Chat Status

chat_querystatus(cstat)

Query the chat status of other nodes:

int chat_querystatus(ref struct _cstat: cstat);

The _cstat struct:

Field Type Description
cstat.task_num int Node number
cstat.avail int Available for chat?
cstat.username string User on that node
cstat.status string Status string

chatstart()

Initiate a chat session with the sysop:

void chatstart();

Modem & Carrier

Function What It Does
carrier() Returns non-zero if carrier detect is present
dcd_check(int: state) Enable (1) or disable (0) carrier-detect checking
mdm_command(string: cmd) Send a modem command string
mdm_flow(int: state) Enable (1) or disable (0) modem flow control
xfertime(int: proto, long: bytes) Estimate transfer time for a file
if (not carrier())
{
  log("Caller dropped carrier during script.");
  return;
}

See Also