Compare commits

..

28 Commits

Author SHA1 Message Date
b5fe2aa64d Update readme.md 2024-03-22 11:17:06 -04:00
5986f861c5 Update readme.md 2024-03-22 11:09:30 -04:00
2c1740cd1c readme cleanup 2024-01-31 20:09:41 -05:00
a91c9064ac i'm a moron 2024-01-29 05:01:31 -06:00
ca5fcc734d Fix wrong message in chat when world has been found 2024-01-29 04:53:11 -06:00
3a758e1ab7 minor improvements 2024-01-29 04:46:28 -06:00
1201257b11 using teleport() causes dementia... smh 2024-01-29 04:39:01 -06:00
5d8b8e0f39 removing the warning thrown until I can figure out how the hell to do that properly 2024-01-29 04:24:32 -06:00
ba4fda62ee wtf is this goofy ass type name???? 2024-01-29 04:24:10 -06:00
8d06546da6 Merge branch 'master' of https://git.worlio.com/bonkmaykr/Tourbot 2024-01-29 04:12:08 -06:00
9b09fb22fd closed #1 2024-01-29 04:11:53 -06:00
89096df226 nope 2024-01-29 04:00:03 -06:00
0dd148f462 attempting fix for #1 2024-01-29 03:55:44 -06:00
55addb09d9 Update readme.md 2024-01-29 00:27:07 -05:00
0b5746e48f Update readme.md 2024-01-28 23:56:18 -05:00
cb388b85d6 update readme 2024-01-28 22:53:43 -06:00
6ffe0bb52b Update readme.md 2024-01-28 23:52:58 -05:00
a5f8cf98ea new conf examples 2024-01-28 22:35:51 -06:00
a8f0a62e33 update cmake lists 2024-01-28 22:22:00 -06:00
f33a6f2e33 teleportation prototyping 2024-01-28 21:10:14 -06:00
9c6d704d60 Update readme.md 2024-01-28 00:34:12 -05:00
c16a516673 Update readme.md 2024-01-28 00:31:57 -05:00
bf60c1f027 Add readme.md 2024-01-28 00:08:12 -05:00
ac5b9a7f7f Merge branch 'master' of https://git.worlio.com/bonkmaykr/Tourbot 2024-01-27 22:52:07 -06:00
959fdfb36b first vs push 2024-01-27 22:51:33 -06:00
c124bfd1e7 initial work in progress 2024-01-27 22:50:07 -06:00
e188d455d7 initial client header 2024-01-27 23:37:57 -05:00
2152441358 initial fork megacommit 2024-01-27 23:37:11 -05:00
12 changed files with 484 additions and 59 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
################################################################################
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################
/.vs
/out/build/x64-Debug

View File

@ -1,8 +1,9 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
project(p3ng0) project(tourbot)
SET(CMAKE_CXX_STANDARD 17) SET(CMAKE_CXX_STANDARD 17)
SET(CMAKE_CXX_STANDARD_REQUIRED True) SET(CMAKE_CXX_STANDARD_REQUIRED True)
SET(CMAKE_CXX_FLAGS "-O3") SET(CMAKE_CXX_FLAGS "-O3")
find_package(CURL REQUIRED) find_package(CURL REQUIRED)
find_package(nlohmann_json 3.2.0 REQUIRED)
add_subdirectory(src) add_subdirectory(src)
install(TARGETS p3ng0 RUNTIME DESTINATION bin) install(TARGETS tourbot RUNTIME DESTINATION bin)

35
CMakeSettings.json Normal file
View File

@ -0,0 +1,35 @@
{
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
},
{
"name": "Linux-GCC-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"cmakeExecutable": "cmake",
"remoteCopySourcesExclusionList": [ ".vs", ".git", "out" ],
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x64" ],
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "$HOME/.vs/${projectDirName}/${workspaceHash}/src",
"remoteBuildRoot": "$HOME/.vs/${projectDirName}/${workspaceHash}/out/build/${name}",
"remoteInstallRoot": "$HOME/.vs/${projectDirName}/${workspaceHash}/out/install/${name}",
"remoteCopySources": true,
"rsyncCommandArgs": "-t --delete",
"remoteCopyBuildOutput": false,
"remoteCopySourcesMethod": "rsync",
"variables": []
}
]
}

View File

@ -4,42 +4,51 @@
messages=conf/messages.list messages=conf/messages.list
# Handles single messages to single phrases. # Handles single messages to single phrases.
replyfile=conf/replies.conf replyfile=conf/replies.conf
# Handles worlds and their names. # Handles worlds and their names. (deprecated)
worldfile=conf/worldlist.conf #worldfile=conf/worldlist.conf
# Look-Up Table which maps aliases to the JSON worlds list
lutfile=conf/lut.conf
# The username you log in as. Account must be registered on worlds servers. # The username you log in as. Account must be registered on worlds servers.
username=Clem username=JohnDoe
# The password to the username above. # The password to the username above.
password=hackme password=123456
# The username who owns the bot. This is for administrative actions. # The username who owns the bot. This is for administrative actions.
owner=Thom owner=SirGrandpa
# The URL to whisper users when asking for bot help. # The URL to whisper users when asking for bot help.
help_url= help_url=
# Bot avatar. Must be VIP for articulated to show up. # Bot avatar. Must be VIP for articulated to show up.
avatar=http://files.worlio.com/users/wirlaburla/avatars/pengobot.mov avatar=http://files.worlio.com/users/bonkmaykr/avatarsforme/gabu1s*4h*4v*.mov
# Room to appear in. # Room to appear in.
room=GroundZero#Reception<dimension-1> room=GroundZero#Reception<dimension-1>
# The position in the world that the bot will sit. # Default world file to "load into"
xpos=0 # As Tourbot is a headless client we aren't actually loading anything,
ypos=0 # this is solely used to allow users to teleport to the bot.
world=http://jett.dacii.net/jett/Recreated%20Worlds/SummersGZ/groundzero%20summers.world
# The position in the world that the bot will sit when idle.
# This should be placed in an easy to reach and clearly visible location.
# Locations will be handled on a per-world basis when teleporting to bookmarks.
xpos=1293
ypos=1710
zpos=0 zpos=0
direction=0 direction=336
# Every 'keep alive', will turn x degrees. # Every 'keep alive', will turn x degrees.
spin=0 spin=10
# Keep alive interval. Setting this too long will make worlds disconnect you. # Keep alive interval. Setting this too long will make worlds disconnect you.
katime=5 katime=5
# The time between random messages. The wait period is between minRandomMsgTime and maxRandomMsgTime. Setting these to 0 will disable. # The time between random messages. The wait period is between minRandomMsgTime and maxRandomMsgTime. Setting these to 0 will disable.
minRandomMsgTime=300 minRandomMsgTime=300
maxRandomMsgTime=900 maxRandomMsgTime=600
# Here are all single-definition messages. For anything that isn't picked in a list, it's here. # Here are all single-definition messages. For anything that isn't picked in a list, it's here.
help_msg=You can find more information here: %s help_msg=You can find more information here: %s
@ -48,6 +57,7 @@ help_whisper_message=%s, you have been whispered with more information.
time_msg=It is %s. time_msg=It is %s.
roll_msg=%s rolled a %i. roll_msg=%s rolled a %i.
world_not_found_msg=Sorry, I don't know that one. world_not_found_msg=Sorry, I don't know that one.
world_found_msg=Taking you there now, teleport to me!
roomusers_msg=There are %i users in this room. roomusers_msg=There are %i users in this room.
conf_reload_msg=My configuration has been reloaded. conf_reload_msg=My configuration has been reloaded.
ping_msg=Response recieved in %ims. ping_msg=Response recieved in %ims.

4
conf.examples/lut.conf Normal file
View File

@ -0,0 +1,4 @@
beachgz=beach
beach gz=beach
partycave=waterworld
party cave=waterworld

50
conf.examples/marks.json Normal file
View File

@ -0,0 +1,50 @@
{
"atlantis": {
"name": "Atlantis",
"url": "http://files.worlio.com/users/aujourd/atlantis/atlantis.world",
"room": "atlantis#beach",
"position": [
"11544",
"7828",
"220",
"203"
],
"blacklist": false
},
"beach": {
"name": "Jimbly's Beach",
"url": "http://ittraining.net/jimbly/groundzero.world",
"room": "Groundzero#Reception",
"position": [
"500",
"500",
"150",
"0"
],
"blacklist": true
},
"mugshots": {
"name": "Jimbly's Mugshot Hallway",
"url": "http://ittraining.net/jimbly/mugshots.world",
"room": "Mugshots#Room0",
"position": [
"106",
"1804",
"150",
"140"
],
"blacklist": true
},
"waterworld": {
"name": "Waterworld, aka Party Cave",
"url": "http://www.ittraining.net/waterworld/waterworld.world",
"room": "Water World#Pool",
"position": [
"672",
"3916",
"150",
"139"
],
"blacklist": false
}
}

View File

@ -1,14 +1,48 @@
[startup] [startup]
Hi, I'm P3NG0, your friendly Worlds bot. *yawn*
My initialization is complete.
[attention] [attention]
hey pengo hey tourbot
hey p3ng0 hey tourbot
p3ng0 tourbot
yo pengo yo tourbot
yo p3ng0 yo tourbot
hello p3ng0 hello tourbot
aloha p3ng0 aloha tourbot
[requestverb]
give me a
give me
give me the
[requestnoun]
take me to
take me to a
take me to the
show me
show me a
show me the
[markreminder]
If you like this world, remember to save a Worldsmark!
[tourtutorial]
Make sure you have "Tourbot" added to your friends list, then click my name and select "Go There" to follow me.
[goto]
I'm teleporting to %s, follow me!
[tour1]
Okay, first stop, we are going to %s! Teleport now
Get ready! We're on our way to %s! Teleport now
[leavingin3]
3 minutes left! Make sure to Worldsmark your favorite ones!
We'll be going to the next world, %s, in 3 minutes!
[leavingin1]
1 minute remaining! Get ready to teleport soon!
1 minute left! If you aren't done exploring, you should save a Worldsmark now.
[leavingsoon]
30 seconds, pack your bags!
30 seconds left.
[tour2]
Next stop, we'll be visiting %s! Teleport now
Get ready to go to our next stop, %s! Teleport now
[tour3]
Final stop, we're travelling to %s! Teleport now
And finally, we'll be arriving at %s! Teleport now
[greets] [greets]
Hi. Hi.
Hi %s. Hi %s.
@ -17,23 +51,32 @@ Nice to meet you %s.
Hope all is well! Hope all is well!
Howdy! Howdy!
Salutations %s. Salutations %s.
Acknowledged.
Added '%s' to database.
[goodbye] [goodbye]
Goodbye! Later!
*fart reverb*
[unknown] [unknown]
Sorry, I don't know what you're asking. Sorry, I don't know what you're asking.
I am unsure of your request. I am unsure of your request.
ERROR: DIVISION BY ZERO Please rephrase.
Need something?
[random] [random]
Beep boop! *yawn*
[null]
Bored? Looking for a new place to go? Artsy? Spooky? Cozy? Say "tourbot, give me a tour"
[jokes] [jokes]
Why did the chicken cross the road? To escape my deadly lasers, of course! The other day, my wife asked me to pass her lipstick, but I accidentally passed her a glue stick. She still isnt talking to me.
The most corrupt CEOs are those of the pretzel companies. Theyre always so twisted.
As I get older, I remember all the people I lost along the way. Maybe a career as a tour guide was not the right choice.
My doctor said I only have 3 weeks to live, so I murdered him and the judge gave me 30 years. Problem solved!
I have many jokes about unemployed people — sadly none of them work.
Youre not completely useless. You can always serve as a bad example.
My boss told me to have a good day. So I went home.
I bought a pair of shoes from a drug dealer. I don't know what he laced them with, but I've been tripping all day.
Teamwork is important; it helps to put the blame on someone else.
The worst part about breaking up with a Japanese woman, is that you have to drop the bomb twice before she understands.
[world] [world]
It's right here: %s It's right here: %s
%s %s
Here is your mark: %s Here is your mark: %s
[whoami] [whoami]
Hi, I'm P3NG0. I'm a friendly bot. I am a BOT designed to help you explore! My source code is based off of P3NG0 by Wirlaburla, we share many of the same functionalities.
I am here to annihilate the human race. I am designed to take you places! Please see https://kangworlds.net/tourbot/ for more information!

View File

@ -1,11 +1,8 @@
hi=Hello. hi=Hello.
any tip=No, no tip. any tip=No thanks.
tour=No deal. tour=Where would you like to go?
bye=Goodbye! bye=Goodbye!
cult=There is no cult on Worlds.com. cult=There is no cult on Worlds.com.
lover=You wouldn't make it past my saw blades, much less the lasers.
love=I have no concept of love.
hate=I have no concept of love.
table flip=(╯°□°)╯︵ ┻━┻ table flip=(╯°□°)╯︵ ┻━┻
flip a table=(╯°□°)╯︵ ┻━┻ flip a table=(╯°□°)╯︵ ┻━┻
(╯°□°)╯︵ ┻━┻=┬─┬ノ( º _ ºノ) (╯°□°)╯︵ ┻━┻=┬─┬ノ( º _ ºノ)

View File

@ -1,3 +1,4 @@
# Deprecated
beach=http://ittraining.net/jimbly/groundzero.world beach=http://ittraining.net/jimbly/groundzero.world
mugshots=http://ittraining.net/jimbly/mugshots.world mugshots=http://ittraining.net/jimbly/mugshots.world
tyler=http://worlio.com/users/dsparil/Tyler%20World/tyler.world tyler=http://worlio.com/users/dsparil/Tyler%20World/tyler.world

82
readme.md Normal file
View File

@ -0,0 +1,82 @@
# Tourbot
### The Friendly Worlds Tour Guide
### Progress: Not finished (teleport needs work)
![Work in Progress](https://files.worlio.com/users/bonkmaykr/http/git/embed/FaFashionAvenueStage8740underconstruction.gif)
Tourbot is an automated chatbot for the Worlds.com online 3D chat service (specifically WorldsPlayer from 1998 and onward). It is based off of P3NG0 by Wirlaburla, the FIRST Worlds chatbot to ever be created and operated live in public. Tourbot retains several features from P3NG0 as well as new features, with the primary selling point being to provide both new and old users an exciting and easy way to explore new worlds, without having to dig through endless archives and file servers like Worlio or jett.dacii.net. This is especially important today as several HOSTs on Worlds have departed or otherwise become inactive, leaving many of the weekly and monthly events abandoned, including tours.
This project is a gift to the Worlds community, and I owe a special thanks to the endless line of nerds who came before me and reverse-engineered the chat protocol so that this could happen.
# To-Do
Underlined tasks are tasks currently being worked on
- [ ] <ins>Core Functions</ins>
- [x] Improved configuration system
- [ ] Strip out old `worldlist.conf` entirely (pair `where` command with `lookUpWorldName()`)
- [ ] <ins>New commands</ins>
- [ ] <ins>Teleport-on-request routine</ins>
- [ ] Basic Tour Guide routine
- [ ] Tour communication (tracking users, prioritize leader commands, whispering users out of range, etc)
- [ ] Ability to join another user's ongoing tour
- [ ] World Database Building
- [ ] Spanish Localization
- [ ] Tour Guide Avatar
- [ ] Contextual Avatars: on tour
- [ ] Contextual Avatars: idle (advertising)
- [ ] Help Pages, Website, and Documentation
- [ ] Tourbot Status Page
- [ ] New User Recognition and Welcome
# Runtime Requirements
- An active IPv4 internet connection
- A valid Worlds.com account which you registered using the WorldsPlayer
- Linux or any other compatible UNIX derivative
- The latest version of glibc
- The actual version required will depend on the version of glibc linked during compile time.
I compile Tourbot on Arch-based distributions which usually have the latest version.
If you use a distro such as Ubuntu or Debian then you may need to compile the bot yourself.
- A working brain
# Building Requirements
- CMake
- GCC
- C++ header/source files from Nlohmann's JSON library
- A few Linux distributions have this library available as an installable package. Please check the repo in your package manager.
- Otherwise you just need to put the required `json.hpp` file in your include path and make sure CMake and GCC can see it.
# Building
## Linux, MINIX and BSD
1. Make sure nlohmann JSON is in your INCLUDE path so your compiler can find it's headers.
- On Arch Linux / endeavourOS: `sudo pacman -Sy nlohmann-json`
- On Ubuntu / Mint / PopOS: `sudo apt-get install nlohmann-json3-dev`
- Other systems: Install and link the library's source files manually.
2. Enter the root directory (where `src` is) and run `cmake -S src/ -B bin/` to create the neccessary files to compile the program.
3. Run `cmake --build bin/` to create an executable file in the bin folder.
4. `cd` to `bin/` and run `./p3ng0`. (Your working directory needs to be the same directory where `conf/` is located.)
## Windows
Tourbot isn't designed for Windows right now. As such, I haven't set up the project files to be compilable on Windows, even with Linux as the target. You can either install WSL and compile there, or set up the compiler yourself if you know what you're doing.
## Mac
I don't use Mac and I never plan to support it, I can't help you.
# FAQ
## When are you going to finish this?
To quote our lord and savior John Carmack, "It will be done when it's done." I'm the only person working on this and I have a real life (crazy I know).
## Can I copy your code?
Yes, but you have to keep the GNU GPL 3 license.
## Where do I report bugs?
Create an issue here on this repository, or send me an email at `bonkmaykr@protonmail.com`.
## Can I deploy this myself on Worlds?
Yes, but please don't try to clog up the servers or chatrooms with a bunch of redundant bots. If Tourbot is working fine, then be nice and let it do it's job unless you can do it better.
I can't stop you from doing whatever you want with it, but if you do bad things your accounts and IP addresses will probably get banned from Worlds, so use Tourbot wisely.
## When I try to log in, the console says "Account is no longer valid".
Double check your spelling.
If you know your username is spelled correctly and the account hasn't been deleted/banned, make sure you saved bot.conf with Unix line endings. Windows line endings use a really funny looking two-byte control code for new lines, and Tourbot doesn't understand how to read them yet. It probably thinks that your username is something like "John<68>" instead of "John". I plan to fix this if a Windows port is ever released, but for now, you will need to be careful with your .conf files.
Many text editors on Windows allow you to change this setting so Tourbot can read your configs correctly, unfortunately the default notepad.exe doesn't include this.
## Why isn't there a Windows version?
P3NG0 was designed from the very beginning with Linux in mind. When it came time to make Tourbot, the same was true; it's designed to be run headless, 24/7, on a server. Meaning you are expected to have a spare computer handy or a cloud VPS rented in order to run the bot. Just about every sane sysadmin uses Linux for headless systems, since Windows was always optimized around it's user interface and not much else.
I could make a Windows version, doing so is actually not very difficult. The code for Tourbot is pretty small and it very rarely interacts with system APIs if at all. It's just a matter of if I want to or not.
## Why are passwords stored in plaintext? That's not safe!
Worlds doesn't have SSL or TLS, doesn't hash passwords, and doesn't have session tokens or revocable sessions. It's a very old program with extremely outdated security. Tourbot isn't secure because the weakest link in the chain makes any security effort on my part pointless. <ins>If you want to remain safe, use a *unique password* for the bot so that it can't be reused in the event it gets stolen.</ins>

View File

@ -9,7 +9,7 @@
#include "acfile.h" #include "acfile.h"
#define BUFFERSIZE 4096 #define BUFFERSIZE 4096
bool debug = false; bool debug = true;
std::random_device rd; std::random_device rd;
std::mt19937 rng(rd()); std::mt19937 rng(rd());
@ -23,6 +23,7 @@ const std::string user_agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.
std::string confFile = "conf/bot.conf"; std::string confFile = "conf/bot.conf";
ACFile* mainConf; ACFile* mainConf;
ACFile* worldlist; ACFile* worldlist;
ACFile* marksLUT;
ACFile* replylist; ACFile* replylist;
AMFile* messages; AMFile* messages;
ALFile* filth; ALFile* filth;
@ -49,7 +50,7 @@ uint16_t roomport = 5672;
std::string login_username; std::string login_username;
std::string login_password; std::string login_password;
std::string avatar = "avatar:pengo.mov"; std::string avatar = "http://files.worlio.com/users/bonkmaykr/avatarsforme/gabu1s*4h*4v*.mov";
// Needs to include dimension too // Needs to include dimension too
std::string room; std::string room;
@ -57,7 +58,7 @@ uint16_t roomID = 1;
int protocol = 24; int protocol = 24;
char* version = "1000000000"; char* version = "1000000000";
int avatars = 253; int avatars = 253; // avoid culling users if possible... what could go wrong?
int keepAliveTime; int keepAliveTime;
uint16_t xPos = 0; uint16_t xPos = 0;
@ -70,6 +71,30 @@ std::map<char, char*> properties;
std::map<char, Drone*> objects; std::map<char, Drone*> objects;
std::vector<Group> groups; std::vector<Group> groups;
namespace internalTypes {
struct dronePositionI {
int x;
int y;
int z;
int yaw;
};
struct dronePositionF {
float x;
float y;
float z;
float yaw;
};
struct markEntry {
std::string name;
std::string url;
std::string room;
dronePositionI position;
bool blacklist;
};
}
void loadConfig(); void loadConfig();
int deinit(int response); int deinit(int response);
void autoInit(); void autoInit();
@ -101,6 +126,10 @@ void sendGroupMessage(Group* g, int *sock, std::string message);
void safeDeleteGroupMember(Group* g, std::string member); void safeDeleteGroupMember(Group* g, std::string member);
void qsend(int *sock, unsigned char str[], bool queue); void qsend(int *sock, unsigned char str[], bool queue);
void handleTeleportRequest(internalTypes::markEntry details);
void handleTour(std::string destination[4]);
void lookUpWorldName(std::string alias, char* buffer);
Drone* getDrone(std::string name) { Drone* getDrone(std::string name) {
for (auto o : objects) for (auto o : objects)
if (o.second->name == name) return o.second; if (o.second->name == name) return o.second;

View File

@ -11,6 +11,7 @@
#include <sstream> #include <sstream>
#include <algorithm> #include <algorithm>
#include <ctime> #include <ctime>
//#include <vector>
#include <ctype.h> #include <ctype.h>
#include <stdlib.h> #include <stdlib.h>
#include <time.h> #include <time.h>
@ -22,6 +23,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
using json = nlohmann::json;
#define CURL_STATICLIB #define CURL_STATICLIB
#include <curl/curl.h> #include <curl/curl.h>
@ -32,6 +34,16 @@
#include "props.h" #include "props.h"
#include "utils.h" #include "utils.h"
// Global variables for tour guide
bool onTour = false; // whether or not we should process the tour routine
std::string tourQueue[4]; // list of world names to teleport to
std::string realLocation; // used to allow teleporting from users
json worldsmarks; // internal World database loaded from marks.json
std::string leader = ""; // whoever requested the current tour
int schedule = 0; // compare against system clock for timing
int tourStep = 0; // which world we're on
class QueuedPacket { class QueuedPacket {
public: public:
QueuedPacket() {}; QueuedPacket() {};
@ -61,9 +73,10 @@ int main(int argc, char const* argv[]) {
printf("info: primary conf file set to \"%s\"\n", confFile.c_str()); printf("info: primary conf file set to \"%s\"\n", confFile.c_str());
} }
loadConfig(); loadConfig();
/*for (auto const& c : mainConf->get()) {
printf("info: (%s),(%s)\n", c.first.c_str(), c.second.c_str()); // initialize teleportation ability
}*/ realLocation = mainConf->getValue("world", "http://jett.dacii.net/jett/Recreated%20Worlds/SummersGZ/groundzero%20summers.world");
autoInit(); autoInit();
while (autoOnline || roomOnline) { while (autoOnline || roomOnline) {
while (!roomOnline) {} // We require the room but get disconnected from auto after awhile while (!roomOnline) {} // We require the room but get disconnected from auto after awhile
@ -84,6 +97,7 @@ int main(int argc, char const* argv[]) {
void loadConfig() { void loadConfig() {
mainConf = new ACFile(confFile.c_str()); mainConf = new ACFile(confFile.c_str());
worldlist = new ACFile(mainConf->getValue("worldfile", "conf/worldlist.conf").c_str()); worldlist = new ACFile(mainConf->getValue("worldfile", "conf/worldlist.conf").c_str());
marksLUT = new ACFile(mainConf->getValue("lutfile", "conf/lut.conf").c_str());
replylist = new ACFile(mainConf->getValue("replyfile", "conf/replies.conf").c_str()); replylist = new ACFile(mainConf->getValue("replyfile", "conf/replies.conf").c_str());
messages = new AMFile(mainConf->getValue("messages", "conf/messages.list").c_str()); messages = new AMFile(mainConf->getValue("messages", "conf/messages.list").c_str());
filth = new ALFile(mainConf->getValue("filthlist", "conf/filth.list").c_str()); filth = new ALFile(mainConf->getValue("filthlist", "conf/filth.list").c_str());
@ -99,6 +113,23 @@ void loadConfig() {
spin = mainConf->getInt("spin", 0); spin = mainConf->getInt("spin", 0);
keepAliveTime = mainConf->getInt("katime", 15); keepAliveTime = mainConf->getInt("katime", 15);
debug = mainConf->getInt("debug", 0) == 1; debug = mainConf->getInt("debug", 0) == 1;
// parse json routine
//
// data structure per entry is as follows:
// name (friendly name used during tours)
// url (the file location)
// room (the chatroom ID for the roomserver)
// position (an array containing the bot's resting position for that world while on tour)
// position is an array with a length of 4, containing X, Y, Z, and yaw in that order.
// blacklist (bool which skips the world during diceroll tours)
// useful for preventing GZ reskins from appearing, exceptions made where appropriate
// blacklisted worlds still need a specified idle position!
//
// feature idea: commentary/notes keyvalue pair which the bot will speak when entering a certain world, used to give tips ??
std::ifstream dictionaryStream("conf/marks.json"); // look for worldsmarks database in the configuration folder and open it
worldsmarks = json::parse(dictionaryStream); // parse and remember data globally
} }
int deinit(int response) { int deinit(int response) {
@ -157,23 +188,11 @@ void roomInit() {
rAutoMsg_t = std::thread(autoRandMessage); rAutoMsg_t = std::thread(autoRandMessage);
} }
void roomKeepAlive() {
sleep(1);
teleport(&roomsock, xPos, yPos, zPos, direction);
while (roomOnline) {
if (direction >= 360) direction += (spin - 360);
else direction+=spin;
longloc(&roomsock, xPos, yPos, zPos, direction);
sleep(keepAliveTime);
}
printf("warning: room keep alive disconnected!\n");
}
void autoRandMessage() { void autoRandMessage() {
int minTime = mainConf->getInt("minRandomMsgTime", 0); int minTime = mainConf->getInt("minRandomMsgTime", 0);
int maxTime = mainConf->getInt("maxRandomMsgTime", 0); int maxTime = mainConf->getInt("maxRandomMsgTime", 0);
int wait = 0; int wait = 600;
sendChatMessage(&roomsock, messages->getMessage("startup")); //sendChatMessage(&roomsock, messages->getMessage("startup"));
if (minTime != 0) { if (minTime != 0) {
while (roomOnline) { while (roomOnline) {
if (wait != 0) { if (wait != 0) {
@ -349,11 +368,15 @@ void reciever(int *sock, uint16_t port, bool* status) {
*status = false; *status = false;
} }
// this only works correctly if using POSIX-compliant line endings in the bot.conf file
// CR+LF breaks everything, should probably fix this if this becomes widely used at any point!
void sessInit(int *sock, std::string username, std::string password) { void sessInit(int *sock, std::string username, std::string password) {
bufout[1] = 0x01; bufout[1] = 0x01;
bufout[2] = CMD_SESSINIT; bufout[2] = CMD_SESSINIT;
int l = 3; int l = 3; // packet construction buffer
std::cout << "Logging in with screenname " + username + "\n"; //debug
// Username // Username
bufout[l++] = 2; bufout[l++] = 2;
bufout[l++] = username.length(); bufout[l++] = username.length();
@ -395,6 +418,7 @@ void sessInit(int *sock, std::string username, std::string password) {
bufout[0] = l; bufout[0] = l;
bufout[l+1] = 0; bufout[l+1] = 0;
qsend(sock, bufout, false); qsend(sock, bufout, false);
} }
@ -755,6 +779,82 @@ bool handleGroups(char* buffer, std::string from, std::string message) {
return false; return false;
} }
// teleport using a markEntry loaded from the database
void handleTeleportRequest(internalTypes::markEntry details) {
std::cout << "info: delaying teleport by 1000ms to avoid race condition\n";
sleep(1);
std::cout << "info: requesting to join room \"" + details.room + "\"\n";
//roomIDReq(&roomsock, details.room); //fatal error
//room = details.room;
std::cout << "info: setting position at " +
std::to_string(details.position.x) + ", " +
std::to_string(details.position.y) + ", " +
std::to_string(details.position.z) + ", " +
std::to_string(details.position.yaw) + "\n";
xPos = details.position.x; // remember so that the idle thread doesn't rubberband us
yPos = details.position.y;
zPos = details.position.z;
direction = details.position.yaw;
teleport(&roomsock, xPos, yPos, zPos, direction); // force positional update immediately
std::cout << "info: updating goto destination\n";
realLocation = details.url;
std::cout << "info: done! initiating watchdog\n";
}
// unimplemented
void handleTour(std::string destination[4]) {
}
// checks performed each heartbeat for the duration of a tour
void tourCaretaker() {
}
// avoid redundant CTRL + V code in handlePhrase()
void lookUpWorldName(std::string alias, char* buffer/*replies*/) {
auto key = worldsmarks.find(alias);
if (key != worldsmarks.end()) {
std::cout << "info: found world \"" + alias + "\" in database, commencing teleport.\n";
internalTypes::markEntry details;
json failsafe = { // leftovers
{"name", "FALLBACK"},
{"url", "rel:GroundZero/GroundZero.world"},
{"room", "GroundZero#Reception"},
{"position", {0, 0, 0, 0}},
{"blacklist", true}
};
if (
typeid((*key)["position"][0]).name() != "int" ||
typeid((*key)["position"][1]).name() != "int" ||
typeid((*key)["position"][2]).name() != "int" ||
typeid((*key)["position"][3]).name() != "int") {
//std::cout << "ERROR: expected type int when reading position. PLEASE CHECK your marks.json!!!\n";
}
details.name = (*key)["name"].get<std::string>();
details.url = (*key)["url"].get<std::string>();
details.room = (*key)["room"].get<std::string>();
details.position.x = (*key)["position"][0];
details.position.y = (*key)["position"][1];
details.position.z = (*key)["position"][2];
details.position.yaw = (*key)["position"][3];
sprintf(buffer, mainConf->getValue("world_found_msg", "Taking you there now, teleport to me!").c_str());
handleTeleportRequest(details);
}
else {
std::cout << "ERROR: no world with the name \"" + alias + "\" exists! aborting teleport request\n";
sprintf(buffer, mainConf->getValue("world_not_found_msg", "Sorry, I don't know that one.").c_str());
}
}
bool handlePhrase(char* buffer, std::string from, std::string message) { bool handlePhrase(char* buffer, std::string from, std::string message) {
std::vector<std::string> args = split(message, ' '); std::vector<std::string> args = split(message, ' ');
if (strcontains("flip a coin", message)) { if (strcontains("flip a coin", message)) {
@ -780,6 +880,35 @@ bool handlePhrase(char* buffer, std::string from, std::string message) {
} }
return true; return true;
} }
// in the interest of making things easier programmatically
// i want to generate LUTs to map phrases to the JSON
// which will allow aliases for each entry in the db
//
// having an admin command like "makelut" to instantly generate these
// by recursively scanning the marks file would be nice
else if (strcontains("take me to", message)) {
std::cout << "info: remote user requested teleportation!\n";
// lookup LUT first for aliases and then check JSON for key
// using worldsmarks.find(message) if no alias present
// spit back error if both are blank
// truncate message to get just the name
std::string truncName = message.erase(message.find("take me to "), 11);
// then perform search
std::string alias = getValueOfIncludedName(marksLUT->get(), truncName);
if (alias.length() > 0) {
std::cout << "info: found world with alias \"" + truncName + "\" in lookup table.\n";
lookUpWorldName(alias, buffer);
}
else { // we didn't find an alias, search the DB directly
std::cout << "info: no world with the alias \"" + truncName + "\" is in the lookup table.\n";
lookUpWorldName(truncName, buffer);
}
return true;
}
return false; return false;
} }
@ -811,6 +940,7 @@ void processText(int *sock, std::string username, std::string message) {
} else if (lowermsg == "ping") { } else if (lowermsg == "ping") {
sprintf(msgout, mainConf->getValue("pong_msg", "Pong!").c_str()); sprintf(msgout, mainConf->getValue("pong_msg", "Pong!").c_str());
sendChatMessage(sock, std::string(msgout)); sendChatMessage(sock, std::string(msgout));
} else if (lowermsg.find("http") != std::string::npos) {
} else if (lowermsg.find("http") != std::string::npos) { } else if (lowermsg.find("http") != std::string::npos) {
int pos; int pos;
if ((pos = lowermsg.find("http://")) != std::string::npos || (pos = lowermsg.find("https://")) != std::string::npos) { if ((pos = lowermsg.find("http://")) != std::string::npos || (pos = lowermsg.find("https://")) != std::string::npos) {
@ -866,6 +996,24 @@ void processWhisper(int *sock, std::string username, std::string message) {
} }
sendWhisperMessage(sock, username, msgout); sendWhisperMessage(sock, username, msgout);
} }
// needs to process geolocation requests and respond with a file path for valid teleporting
if (lowermsg == "&|+where?") {
// intentionally omit bot location if we're touring so players always start at world start
// the bot should ideally be placed in the same starting room, within the player's view as soon as they spawn
//
// doing it this way requires that we handle bookmark room IDs separately from the destination URL
if (onTour == true) {
std::cout << "info: touring, sending truncated location\n";
sendWhisperMessage(sock, username, "&|+where>" + realLocation + "#" + room);
}
else {
// are all these concats really fucking neccessary?
std::cout << "info: not touring, sending full location\n";
sendWhisperMessage(sock, username, "&|+where>" + realLocation + "#" + room
+ "@" + std::to_string(xPos) + "," + std::to_string(yPos) + "," + std::to_string(zPos));
}
}
} }
void sendChatMessage(int *sock, std::string msg) { void sendChatMessage(int *sock, std::string msg) {
@ -950,3 +1098,22 @@ void qsend(int *sock, unsigned char str[], bool queue) {
qp->flush(0); qp->flush(0);
} }
// heartbeat needed to prevent disconnects
// also used to track time
void roomKeepAlive() {
sleep(1);
teleport(&roomsock, xPos, yPos, zPos, direction);
while (roomOnline) {
// force update position to prevent a disconnect
if (direction >= 360) direction += (spin - 360);
else direction += spin;
longloc(&roomsock, xPos, yPos, zPos, direction);
// do routine checks and call scheduled events
if (onTour == true) {tourCaretaker();}
// wait until next heartbeat
sleep(keepAliveTime);
}
printf("warning: room keep alive disconnected!\n");
}