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 Management
- Privilege & Class
- User File Operations
- Caller Log
- Session Info
- Chat Status
- Modem & Carrier
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
- Variables & Types — User Record
— every field in the
usrstruct - Display & I/O — output, input, and file functions
- Message & File Areas — area navigation and message access