diff --git a/src/sbbs3/bulkmail.cpp b/src/sbbs3/bulkmail.cpp
index dab3bc53f0cbce064f31073e10946076f12d93c6..22d4741d32658d4a48d80707e434fb8c24778aab 100644
--- a/src/sbbs3/bulkmail.cpp
+++ b/src/sbbs3/bulkmail.cpp
@@ -207,13 +207,13 @@ int sbbs_t::bulkmailhdr(smb_t* smb, smbmsg_t* msg, uint usernum)
 		if(node.useron==usernum && !(node.misc&NODE_POFF)
 			&& (node.status==NODE_INUSE || node.status==NODE_QUIET)) {
 			SAFEPRINTF2(str,text[EmailNodeMsg],cfg.node_num,useron.alias);
-			putnmsg(&cfg,i,str);
+			putnmsg(i,str);
 			break; 
 		} 
 	}
 	if(i>cfg.sys_nodes) {   /* User wasn't online, so leave short msg */
 		SAFEPRINTF(str,text[UserSentYouMail],useron.alias);
-		putsmsg(&cfg,usernum,str); 
+		putsmsg(usernum,str);
 	}
 	return(0);
 }
diff --git a/src/sbbs3/chat.cpp b/src/sbbs3/chat.cpp
index 2275eb698499d439fc3d34afc26c14922c33777d..ff934d5d287928e85a3e6d85df49bdbf2f14c158 100644
--- a/src/sbbs3/chat.cpp
+++ b/src/sbbs3/chat.cpp
@@ -349,7 +349,7 @@ void sbbs_t::multinodechat(int channel)
 						SAFECAT(buf,crlf);
 						if(useron.chat&CHAT_ECHO)
 							bputs(buf);
-						putnmsg(&cfg,j,buf);
+						putnmsg(j,buf);
 						break;
 					case 'Q':	/* quit */
 						done=1;
@@ -479,7 +479,7 @@ void sbbs_t::multinodechat(int channel)
 									? text[UNKNOWN_USER] : useron.alias
 									,"you");
 								SAFECAT(buf,crlf);
-								putnmsg(&cfg,usr[j],buf);
+								putnmsg(usr[j],buf);
 							}
 
 
@@ -494,11 +494,11 @@ void sbbs_t::multinodechat(int channel)
 								if(i==j)
 									continue;
 								getnodedat(usr[i],&node,0);
-								putnmsg(&cfg,usr[i],buf);
+								putnmsg(usr[i],buf);
 							}
 							for(i=0;i<qusrs;i++) {
 								getnodedat(qusr[i],&node,0);
-								putnmsg(&cfg,qusr[i],buf);
+								putnmsg(qusr[i],buf);
 							}
 							continue;
 						}
@@ -513,11 +513,11 @@ void sbbs_t::multinodechat(int channel)
 						bputs(buf);
 					for(i=0;i<usrs;i++) {
 						getnodedat(usr[i],&node,0);
-						putnmsg(&cfg,usr[i],buf);
+						putnmsg(usr[i],buf);
 					}
 					for(i=0;i<qusrs;i++) {
 						getnodedat(qusr[i],&node,0);
-						putnmsg(&cfg,qusr[i],buf);
+						putnmsg(qusr[i],buf);
 					}
 					if(!usrs && channel && gurubuf
 						&& cfg.chan[channel-1]->misc&CHAN_GURU)
@@ -734,7 +734,7 @@ void sbbs_t::privchat(bool forced, int node_num)
 				sprintf(str,text[NodePChatPageMsg]
 					,cfg.node_num,thisnode.misc&NODE_ANON
 						? text[UNKNOWN_USER] : useron.alias);
-				putnmsg(&cfg,n,str);
+				putnmsg(n,str);
 				sprintf(str,"paged %s on node %d to private chat"
 					,username(&cfg,node.useron,tmp),n);
 				logline("C",str);
@@ -1379,7 +1379,7 @@ void sbbs_t::nodemsg()
 					CRLF;
 					break;
 				}
-				putsmsg(&cfg,usernumber,buf);
+				putsmsg(usernumber,buf);
 				sprintf(str,"sent telegram to %s #%u"
 					,username(&cfg,usernumber,tmp),usernumber);
 				logline("C",str);
@@ -1409,7 +1409,7 @@ void sbbs_t::nodemsg()
 						sprintf(buf,text[NodeMsgFmt],cfg.node_num
 							,thisnode.misc&NODE_ANON
 								? text[UNKNOWN_USER] : useron.alias,line);
-						putnmsg(&cfg,i,buf);
+						putnmsg(i,buf);
 						if(!(node.misc&NODE_ANON))
 							bprintf(text[MsgSentToUser],"Message"
 								,username(&cfg,usernumber,tmp),usernumber);
@@ -1433,7 +1433,7 @@ void sbbs_t::nodemsg()
 						if((node.status==NODE_INUSE
 							|| (SYSOP && node.status==NODE_QUIET))
 							&& (SYSOP || !(node.misc&NODE_POFF)))
-							putnmsg(&cfg,i,buf);
+							putnmsg(i,buf);
 					}
 					SAFECOPY(str,"sent message to all nodes");
 					logline("C",str);
diff --git a/src/sbbs3/email.cpp b/src/sbbs3/email.cpp
index dc18614cc70c3e6c8320085417f43f3ac55d770e..1668d01c9c0c0b3e906c469c4d59b92bc3873e44 100644
--- a/src/sbbs3/email.cpp
+++ b/src/sbbs3/email.cpp
@@ -326,13 +326,13 @@ bool sbbs_t::email(int usernumber, const char *top, const char *subj, int mode,
 		if(node.useron==usernumber && !(node.misc&NODE_POFF)
 			&& (node.status==NODE_INUSE || node.status==NODE_QUIET)) {
 			safe_snprintf(str,sizeof(str),text[EmailNodeMsg],cfg.node_num,useron.alias);
-			putnmsg(&cfg,i,str);
+			putnmsg(i,str);
 			break; 
 		} 
 	}
 	if(i>cfg.sys_nodes) {	/* User wasn't online, so leave short msg */
 		safe_snprintf(str,sizeof(str),text[UserSentYouMail],useron.alias);
-		putsmsg(&cfg,usernumber,str); 
+		putsmsg(usernumber,str);
 	}
 	return(true);
 }
diff --git a/src/sbbs3/file.cpp b/src/sbbs3/file.cpp
index 24513c3ae8c471bd7c71149c54e7eaec20912924..852dc72728b696f721b96cd5bb4359c169c69a05 100644
--- a/src/sbbs3/file.cpp
+++ b/src/sbbs3/file.cpp
@@ -181,7 +181,7 @@ bool sbbs_t::removefcdt(file_t* f)
 		sprintf(str,"%lu minute",cdt);
 		sprintf(tmp,text[FileRemovedUserMsg]
 			,f->name,cdt ? str : text[No]);
-		putsmsg(&cfg,u,tmp);
+		putsmsg(u,tmp);
 	}
 	else {
 		if(cfg.dir[f->dir]->misc&DIR_CDTUL)
@@ -201,7 +201,7 @@ bool sbbs_t::removefcdt(file_t* f)
 		adjustuserval(&cfg, u, USER_CDT, -cdt);
 		sprintf(tmp,text[FileRemovedUserMsg]
 			,f->name,cdt ? ultoac(cdt,str) : text[No]);
-		putsmsg(&cfg,u,tmp);
+		putsmsg(u,tmp);
 	}
 
 	adjustuserval(&cfg, u, USER_ULB, -f->size);
diff --git a/src/sbbs3/logon.cpp b/src/sbbs3/logon.cpp
index 99c7cf109936a7418a46effe20d22b92c18f78b9..d9db5d82523cb8e9ec9d9a27b03841ef63ac85ea 100644
--- a/src/sbbs3/logon.cpp
+++ b/src/sbbs3/logon.cpp
@@ -549,7 +549,7 @@ bool sbbs_t::logon()
 					,cfg.node_num
 					,thisnode.misc&NODE_ANON ? text[UNKNOWN_USER] : useron.alias
 					,connection);
-				putnmsg(&cfg,i,str); 
+				putnmsg(i, str);
 			} 
 		}
 
diff --git a/src/sbbs3/logout.cpp b/src/sbbs3/logout.cpp
index e9cf622c49c23cd3edd3eef8f6e54c794fdfe00a..c3de6296bf0ea6bdbf401d4234a265c159e65689 100644
--- a/src/sbbs3/logout.cpp
+++ b/src/sbbs3/logout.cpp
@@ -69,7 +69,7 @@ void sbbs_t::logout()
 					SAFEPRINTF2(str,text[NodeLoggedOff],cfg.node_num
 						,thisnode.misc&NODE_ANON
 						? text[UNKNOWN_USER] : useron.alias);
-					putnmsg(&cfg,i,str); 
+					putnmsg(i, str);
 				}
 			}
 
diff --git a/src/sbbs3/mail.cpp b/src/sbbs3/mail.cpp
index c067bc038776ba2eb532e54cfb5526a8f70dd5c6..c3ceb9797703d92679718abca57c0485136759d7 100644
--- a/src/sbbs3/mail.cpp
+++ b/src/sbbs3/mail.cpp
@@ -143,7 +143,7 @@ void sbbs_t::telluser(smbmsg_t* msg)
 			sprintf(str
 				,text[UserReadYourMailNodeMsg]
 				,cfg.node_num,useron.alias);
-			putnmsg(&cfg,n,str);
+			putnmsg(n,str);
 			break; 
 		} 
 	}
@@ -151,7 +151,7 @@ void sbbs_t::telluser(smbmsg_t* msg)
 		now=time(NULL);
 		sprintf(str,text[UserReadYourMail]
 			,useron.alias,timestr(now));
-		putsmsg(&cfg,usernumber,str); 
+		putsmsg(usernumber,str);
 	}
 }
 
diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp
index 714575b3513e5038b3d4375dd3892019e4cd2da1..310dedbb5fa45c22c9afd92b8a9a91849f04b1f9 100644
--- a/src/sbbs3/main.cpp
+++ b/src/sbbs3/main.cpp
@@ -4556,7 +4556,7 @@ void sbbs_t::daily_maint(void)
 
 		if(!(user.misc&(DELETED|INACTIVE))
 			&& user.expire && (uint)user.expire<=(uint)now) {
-			putsmsg(&cfg,user.number,text[AccountHasExpired]);
+			putsmsg(user.number,text[AccountHasExpired]);
 			SAFEPRINTF2(str,"DAILY: %s #%u Expired",user.alias,user.number);
 			lputs(LOG_NOTICE, str);
 			if(cfg.level_misc[user.level]&LEVEL_EXPTOVAL
diff --git a/src/sbbs3/postmsg.cpp b/src/sbbs3/postmsg.cpp
index 194b5f6c21fec7f1f7b9547a3790ef4615a80003..0f621086d0af075efef4e4e39ac40c0b466cf775 100644
--- a/src/sbbs3/postmsg.cpp
+++ b/src/sbbs3/postmsg.cpp
@@ -350,7 +350,7 @@ bool sbbs_t::postmsg(int subnum, int wm_mode, smb_t* resmb, smbmsg_t* remsg)
 				,cfg.sub[subnum]->misc&SUB_NAME ? useron.name : useron.alias
 				,connection
 				,cfg.grp[cfg.sub[subnum]->grp]->sname,cfg.sub[subnum]->lname);
-			putsmsg(&cfg, i, str);
+			putsmsg(i, str);
 		}
 	}
 
diff --git a/src/sbbs3/putmsg.cpp b/src/sbbs3/putmsg.cpp
index 9b87afacdfd35d645d5adf6f3c8bf489c3be33d4..8fd446289ce14050f854888d6548c5be16db3172 100644
--- a/src/sbbs3/putmsg.cpp
+++ b/src/sbbs3/putmsg.cpp
@@ -552,3 +552,17 @@ char sbbs_t::putmsgfrag(const char* buf, int& mode, int org_cols, JSObject* obj)
 
 	return str[l];
 }
+
+// Write short message (telegram) to user, supports @-codes
+bool sbbs_t::putsmsg(int user_num, const char* str)
+{
+	char buf[256];
+	return ::putsmsg(&cfg, user_num, expand_atcodes(str, buf, sizeof buf)) == 0;
+}
+
+// Write short message to node in-use, supports @-codes
+bool sbbs_t::putnmsg(int node_num, const char* str)
+{
+	char buf[256];
+	return ::putnmsg(&cfg, node_num, expand_atcodes(str, buf, sizeof buf)) == 0;
+}
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index 8115232e2dffe0272762a1cef459e36e5f7b0659..9ca0dd785f4a8347893fa3ac9c177ca9d3042cd2 100644
--- a/src/sbbs3/sbbs.h
+++ b/src/sbbs3/sbbs.h
@@ -768,14 +768,18 @@ public:
 	void	time_bank(void);
 	void	change_user(void);
 
+	/* putmsg.cpp */
+	char	putmsg(const char *str, int mode, int org_cols = 0, JSObject* obj = NULL);
+	char	putmsgfrag(const char* str, int& mode, int org_cols = 0, JSObject* obj = NULL);
+	bool	putnmsg(int node_num, const char*);
+	bool	putsmsg(int user_num, const char*);
+
 	/* writemsg.cpp */
 	void	automsg(void);
 	bool	writemsg(const char *str, const char *top, char *subj, int mode, int subnum
 				,const char *to, const char* from, const char** editor=NULL, const char** charset=NULL);
 	char*	quotes_fname(int xedit, char* buf, size_t len);
 	char*	msg_tmp_fname(int xedit, char* fname, size_t len);
-	char	putmsg(const char *str, int mode, int org_cols = 0, JSObject* obj = NULL);
-	char	putmsgfrag(const char* str, int& mode, int org_cols = 0, JSObject* obj = NULL);
 	bool	msgabort(bool clear = false);
 	void	clearabort() { sys_status &= ~SS_ABORT; }
 	bool	email(int usernumber, const char *top = NULL, const char *title = NULL
diff --git a/src/sbbs3/un_qwk.cpp b/src/sbbs3/un_qwk.cpp
index 91fe0918518e63fe4e8fc8554eda0866a752806e..e051c08c421b1dd9a20a3ac2b6bd436fdcd777b2 100644
--- a/src/sbbs3/un_qwk.cpp
+++ b/src/sbbs3/un_qwk.cpp
@@ -251,7 +251,7 @@ bool sbbs_t::unpack_qwk(char *packet,uint hubnum)
 			if(qwk_import_msg(qwk, (char *)block, blocks, hubnum+1, &smb, usernum, &msg, &dupe)) {
 				lprintf(LOG_INFO,"Imported QWK mail message from %s to %s #%u", msg.from, msg.to, usernum);
 				SAFEPRINTF(str,text[UserSentYouMail],msg.from);
-				putsmsg(&cfg,usernum,str);
+				putsmsg(usernum,str);
 				tmsgs++;
 			} else {
 				if(dupe)
@@ -337,7 +337,7 @@ bool sbbs_t::unpack_qwk(char *packet,uint hubnum)
 					,msg.from
 					,cfg.qhub[hubnum]->id
 					,cfg.grp[cfg.sub[j]->grp]->sname, cfg.sub[j]->lname);
-				putsmsg(&cfg, destuser, str);
+				putsmsg( destuser, str);
 			}
 		} else {
 			if(dupe)
@@ -399,7 +399,7 @@ bool sbbs_t::unpack_qwk(char *packet,uint hubnum)
 		SAFEPRINTF2(fname,"%s/%s",inbox,dirent->d_name);
 		mv(str,fname,1 /* overwrite */);
 		sprintf(str,text[ReceivedFileViaQWK],dirent->d_name,cfg.qhub[hubnum]->id);
-		putsmsg(&cfg,1,str);
+		putsmsg(1,str);
 		lprintf(LOG_INFO,"Received file from %s: %s", cfg.qhub[hubnum]->id, dirent->d_name);
 	}
 	if(dir!=NULL)
diff --git a/src/sbbs3/un_rep.cpp b/src/sbbs3/un_rep.cpp
index 23bb28e1b4b91e9a628b8f3955a9230420c50094..1ddb6657e0c59981e779e19af4fba70715745a34 100644
--- a/src/sbbs3/un_rep.cpp
+++ b/src/sbbs3/un_rep.cpp
@@ -340,14 +340,14 @@ bool sbbs_t::unpack_rep(char* repfile)
 							|| node.status==NODE_QUIET)) {
 							SAFEPRINTF2(str,text[EmailNodeMsg]
 								,cfg.node_num,msg.from);
-							putnmsg(&cfg,k,str);
+							putnmsg(k,str);
 							break;
 						}
 					}
 				}
 				if(cfg.node_num==0 || k>cfg.sys_nodes) {
 					SAFEPRINTF(str,text[UserSentYouMail],msg.from);
-					putsmsg(&cfg,usernum,str);
+					putsmsg(usernum,str);
 				}
 				tmsgs++;
 			} else {
@@ -498,7 +498,7 @@ bool sbbs_t::unpack_rep(char* repfile)
 						,msg.from
 						,(useron.rest&FLAG('Q')) ? useron.alias : "QWK"
 						,cfg.grp[cfg.sub[n]->grp]->sname, cfg.sub[n]->lname);
-					putsmsg(&cfg, destuser, str);
+					putsmsg(destuser, str);
 				}
 				if(!(useron.rest&FLAG('Q')))
 					user_event(EVENT_POST);
@@ -607,7 +607,7 @@ bool sbbs_t::unpack_rep(char* repfile)
 			SAFEPRINTF2(fname,"%s/%s",inbox,dirent->d_name);
 			mv(str,fname,1);
 			SAFEPRINTF2(str,text[ReceivedFileViaQWK],dirent->d_name,useron.alias);
-			putsmsg(&cfg,1,str);
+			putsmsg(1,str);
 			lprintf(LOG_NOTICE, "Received file: %s", dirent->d_name);
 		}
 		if(dir!=NULL)
diff --git a/src/sbbs3/writemsg.cpp b/src/sbbs3/writemsg.cpp
index 901c61e1632cfb6e410341501831b8a079f77acd..5600ef8f3b7ef1c1de57b7e121fb878775e65005 100644
--- a/src/sbbs3/writemsg.cpp
+++ b/src/sbbs3/writemsg.cpp
@@ -1592,13 +1592,13 @@ bool sbbs_t::forwardmsg(smb_t* smb, smbmsg_t* orgmsg, const char* to, const char
 			if(node.useron==usernumber && !(node.misc&NODE_POFF)
 				&& (node.status==NODE_INUSE || node.status==NODE_QUIET)) {
 				SAFEPRINTF2(str,text[EmailNodeMsg],cfg.node_num,useron.alias);
-				putnmsg(&cfg,i,str);
+				putnmsg(i,str);
 				break;
 			}
 		}
 		if(i>cfg.sys_nodes) {	/* User wasn't online, so leave short msg */
 			SAFEPRINTF(str,text[UserSentYouMail],useron.alias);
-			putsmsg(&cfg,usernumber,str);
+			putsmsg(usernumber,str);
 		}
 	} else {
 		if(net_type == NET_FIDO && cfg.netmail_sem[0])