A different approach to 'Currency and Economy'

I just saw the bot online, so I was able to test it out a bit. That’s pretty cool.

How does one get more money, is it a per/hour thing?

Would you be interested in Posting the Banker up in the Bank I have in Umber Point?

1 Like

At the moment it has a storage for your money, allow you to send money between characters. I am currently working on updating the client as the bot keeps going to sleep on its own >w< I also need to finish rolling out the new update. I need some sleep quickly then I’ll get finish that in the morning and :crossed_fingers: it should be online before I have work again.

As for locations I already have a bank but I need to update the bot to allow it to handle player arrive and leave messages before I can join that bank with the bot to the map.

A more technical explanation would be that it

  1. Checks that the message is not from itself
  2. Checks the type is a message, else message the player to say that only messages are accepted
  3. Splits the message into tokens
  4. Uses those tokens to interpreted the command
  5. If the command is in the list of possible check commands then
  6. Check if the character is in the storage if not add them and set their starting Ballance to max(0, MAX_IN_CIRCULATION // 1000)
  7. Send them their Ballance
  8. If the message is in the list of possible send commands
  9. If it is then check all the required tokens are available
  10. If not let the player know. If they are then we add the sender’s name and character id to an awaiting list
  11. Send a getChar request to the server for the characters name in the token
  12. Upon recieving the message from the server about the info of the sent character name we get the stored info then add amount to the received characters balance. And take away the amount from the other character then let them both know about the transaction.
  13. However if an error occured or the request timed out or the character name was invalid then reset both balances to how they used to be and let the sender know.
  14. If the command was a suggest or a report then append the report or suggestion to it’s respective file.
  15. Save all of the storage to prevent data loss.
  16. Ping the server to stay awake.
  17. Clean the awaiting list(the list that we used to get a sort of asynchronous getChar with the send command.
  18. Log all steps taken to the console to keep track of any errors.

That’s pretty cool, I have to say. Me and some friends have been messing around with it.

How does money accumulate?

1 Like

Well as each new character signs up the amount of money within sinder begins to grow untill it hits 1,000,000Sb in circulation at which point no more will come in. This of course can be changed but would require more than 1000 people to sign up but because of the fall off it would be even more than that.

At some point, if I think more money in circulation is necessary I will double everybody’s balance. But that is only if lots of people think it is necessary.

I may add interest to stored money but that is a future part of the bot.

If you are interested this is the code thus far.

import MuckletBot
import json
import time

CHECK_BALANCE_COMMANDS = ["check", "balance", "inquire"]
SEND_COMMANDS = ["send", "pay", "give"]

MAX_IN_CIRCULATION = 1_000_000

HELP_TEXT = """
Hello, I am the currency bot. I can help you send money to other characters and check your balance.

To check your balance, type `balance` or `check`.
Note that you start with 1000 coins.

To send money to another character, type `send` or `pay` followed by the amount of money you want to send and the **full name** of the character you want to send it to.

Please note that you must message me the command.

Examples:
`message Banker = balance` - check your balance.
`message Banker = send 1000 Ico Twilight` - send 1000 coins to the character Ico Twilight.
`message Banker = pay 1000 Ico Twilight` - send 1000 coins to the character Ico Twilight.
`message Banker = check` - check your balance.

If you have any questions, please contact the owner of the bot.

Thank you for using the currency bot.

- The currency bot
"""


class Logger:

	def __init__(self, logfile="logs.txt", indent=6, wrapping=" "):
		self.logfile = logfile
		self.indent = indent
		self.wrapping = wrapping
		self.i = 0

	def __call__(self, *messages, title=""):
		self.log(*messages, title=title)

	def log(self, *messages, title=""):
		string = self.wrapping * 5 + " " + title + " " + self.wrapping * 5 + "\n"
		for message in messages:
			self.i += 1
			string += str(self.i) + ":" + " " * self.indent + str(message) + "\n"
		string += self.wrapping * 6 + self.wrapping * len(
		 title) + self.wrapping * 6 + "\n"

		if self.logfile is not None:
			with open(self.logfile, "a") as f:
				f.write(string)
		print(string)


logger = Logger()


class STORAGE:

	def __init__(self, init):
		with open(init, "r") as f:
			storage = json.load(f)
		self.storage = storage
		self.password = "This isn't the password"

	def add(self, character):
		if character.id not in self.storage:
			total = self.getTotal()
			give = max((MAX_IN_CIRCULATION - total) // 1000, 0)
			self.set(character, give)
			logger.log(
			 f"Added [{character.name}] to the storage with [{give}] Sinder Bucks.",
			 title="STORAGE")
			self.save()
		else:
			return

	def get(self, character):
		self.add(character)
		return self.storage[character.id]["balance"]

	def set(self, character, value):
		self.storage[character.id] = {"balance": value}
		self.save()

	def transfer(self, amount, sender, receiver):
		self.add(sender)
		self.add(receiver)
		self.storage[sender.id]["balance"] -= amount
		self.storage[receiver.id]["balance"] += amount
		logger.log(
		 f"Transferred [{amount}] Sinder Bucks from [{sender.name}] to [{receiver.name}].",
		 title="STORAGE")
		self.save()

	def getTotal(self):
		total = 0
		for key, value in self.storage.items():
			print(key, value)
			total += value["balance"]
		return total

	def save(self):
		with open("storage.json", "w") as f:
			json.dump(self.storage, f)

	def setPassword(self, password):
		self.password = password


storage = STORAGE("storage.json")


# the function that gets called when a message is recieved
def OnMessage(bot, message):
	# if the message type is either a say, pose, whisper, address or a message
	if message.get("type") in ["message"]:
		# if the recieved message is not from ourself
		if message.get("sender").id != bot.CID:
			command = message.get("message").strip().split(" ")
			if len(command) == 0:
				return
			if command[0] in CHECK_BALANCE_COMMANDS:
				message.get("sender").message(
				 f"You have {storage.get(message.get('sender'))} Sinder Bucks.")
			elif command[0] in SEND_COMMANDS:
				if len(command) < 4:
					message.get("sender").message(
					 "You need to specify the amount and then the recievers full name.")
					return
				if command[1].isnumeric() and int(command[1]) > 0 and int(
				  command[1]) == float(command[1]):
					if storage.get(message.get("sender")) >= int(command[1]):
						# get the reciever
						reciever = bot.getCharID(" ".join(command[2:]))
						if reciever is None:
							message.get("sender").message(
							 "The reciever could not be found, please make sure you use the full name."
							)
							return
						reciever = MuckletBot.Character(bot, " ".join(command[2:]), reciever)
						storage.transfer(int(command[1]), message.get("sender"), reciever)
						message.get("sender").message(
						 f"You sent {command[1]} Sinder Bucks to {reciever.name}.")
						reciever.message(
						 f"You recieved {command[1]} Sinder Bucks from {message.get('sender').name}."
						)
					else:
						message.get("sender").message(
						 f"You only have {storage.get(message.get('sender'))} Sinder Bucks.")
						return
				else:
					message.get("sender").message(
					 "The amount must be a positive integer with no decimals or letters.")
					return
			elif command[0] == "help":
				message.get("sender").message(HELP_TEXT)
			elif command[0] == "total":
				if len(command) < 2:
					message.get("sender").message(f"Please provide a password.")
					return
				if " ".join(command[1:]) == storage.password:
					message.get("sender").message(
					 f"The total amount of Sinder Bucks in circulation is {storage.getTotal()}."
					)
				else:
					message.get("sender").message(f"Invalid password.")

			elif command[0] == "set":
				if len(command) < 5:
					message.get("sender").message(
					 f"Please provide a charactername, amount and password.")
					return
				if " ".join(command[4:]) == storage.password:
					if command[3].isnumeric() and int(command[3]) > 0 and int(
					  command[3]) == float(command[3]):
						# get the reciever
						reciever = bot.getCharID(" ".join(command[1:3]))
						if reciever is None:
							message.get("sender").message(
							 f"The reciever could not be found, please make sure you use the full name."
							)
							return
						reciever = MuckletBot.Character(bot, " ".join(command[1:3]), reciever)
						storage.set(reciever, int(command[3]))
						message.get("sender").message(
						 f"You set {reciever.name}'s balance to {command[3]} Sinder Bucks.")
					else:
						message.get("sender").message(
						 "The amount must be a positive integer with no decimals or letters.")
						return
				else:
					message.get("sender").message(f"Invalid password.")

			elif command[0] == "get":
				if len(command) < 4:
					message.get("sender").message(
					 f"Please provide a charactername and password.")
					return
				if " ".join(command[3:]) == storage.password:
					# get the reciever
					reciever = bot.getCharID(" ".join(command[1:3]))
					if reciever is None:
						message.get("sender").message(
						 f"The reciever could not be found, please make sure you use the full name."
						)
						return
					reciever = MuckletBot.Character(bot, " ".join(command[1:3]), reciever)
					message.get("sender").message(
					 f"{reciever.name} has {storage.get(reciever)} Sinder Bucks.")
				else:
					message.get("sender").message(f"Invalid password.")
			elif command[0] == "report":
				if len(command) < 2:
					message.get("sender").message(f"Please provide a report")
					return
				# add the report to the report file
				with open("reports.txt", "a") as f:
					f.write(f"{message.get('sender').name}: {' '.join(command[1:])}\n")
				message.get("sender").message(f"Your report has been submitted.")
			elif command[0] == "suggest":
				if len(command) < 2:
					message.get("sender").message(f"Please provide a suggestion")
					return
				# add the report to the features file
				with open("features.txt", "a") as f:
					f.write(f"{message.get('sender').name}: {' '.join(command[1:])}\n")
				message.get("sender").message(f"Your suggestion has been submitted.")

			else:
				message.get("sender").message(
				 f"Invalid command, please use `help` for a list of commands.")


# the function that gets called in a seperate thread when the bot is fully booted
def OnOpen(bot):
	method = "call." + bot.RID + ".getChar"
	result = bot.request(bot.ws,
	                     method,
	                     params={"charID": "cevi2oe9gbrtr77qto90"})
	bot.Logger(result, title="RECIEVED RESULT")
	while (1):
		bot.say("1")
		bot.ping()
		time.sleep(120)


def OnError(bot, error):
	# any errors that occur without an atached promise will be sent here
	return
	bot.Logger(str(error), title="RECIEVED ERROR WITHOUT PROMISE")


if __name__ == "__main__":
	# create a new bot
	bot = MuckletBot.Bot(TOKEN="The Bots Token",
	                     VERBOSE=True,
	                     HOST='wss://api.wolfery.com',
	                     ORIGIN='https://wolfery.com')
	# set the onMessage event handler
	bot.onMessage = OnMessage
	# set the onStart event handler
	bot.onOpen = OnOpen
	# set the onError event handler
	bot.onError = OnError
	# start the bot
	bot.run_forever()
	# note if you want to run 1 < bots in the same script, you need to use run() instead of run_forever()

Excuse it’s messiness

int(command[1]) == float(command[1])

:face_with_raised_eyebrow:

1 Like

… >w<

p.s. The bot update has finished, If you are interested in the changes then here they are:

  1. better support for upcoming features.
  2. better help text.
  3. admin support (I will be looking for someone who would be interested in this).
  4. better promise’s
  5. a clean look to the outputs
  6. alias’s (listed in the help text).
  7. new code ^w^
import MuckletBot, math, json, time

CHECK_COMMANDS = ["check", "c", "balance", "bal", "b", "inquire", "i", "query", "q"]
SEND_COMMANDS = ["send", "s", "give", "g", "transfer", "t", "pay", "p"]

MAX_IN_CIRCULATION = 1_000_000

def Log(message): print(f"[{time.strftime('%H:%M:%S')}] {message}")

class STORAGE:
    def __init__(self, data): self.data = data; self.password = "not the actual password"
    def get(self, id):
        self.add(id)
        return self.data.get(id).get("balance")
    def add(self, id):
        if self.data.get(id) is None: Log(f"Added user [{id}] to the storage.")
        if self.data.get(id) is None: self.data[id] = {"balance": max((MAX_IN_CIRCULATION - self.getTotal()) // 1000, 0)}
    def set(self, id, amount):
        self.add(id)
        self.data[id]["balance"] = amount
        Log(f"Set user [{id}]'s balance to [{amount}].")
    def getTotal(self):
        total = 0
        for id in self.data: total += self.data[id]["balance"]
        return total
    def multiplyAll(self, multiplier):
        for id in self.data: self.data[id]["balance"] = math.floor(self.data[id]["balance"] * multiplier)
        Log(f"Multiplied all balances by [{multiplier}].")
    def save(self):
        with open("storage.json", "w") as f: json.dump(self.data, f)

storage = STORAGE(json.load(open("storage.json", "r")))

def OnBotMessage(bot = None, data = None):
    if data.get("type") != "message":
        bot.message(data.get("sender"), f"[{data.get('message')}] is an invalid command type, I only accept messages.")
        # Log(f"Invalid command type [{data.get('type')}] from [{data.get('sender').name}].")
    else:
        Log (f"-- [Received command [{data.get('message')}] from [{data.get('sender').name}]]")
        commands = data.get("message").split(" ")
        if commands[0] in CHECK_COMMANDS:
            bot.message(data.get("sender"), f" You have [{str(storage.get(data.get('sender').id))}] SinderBucks.")
            Log(f"{data.get('sender').name} has [{str(storage.get(data.get('sender').id))}] SinderBucks.")
        elif commands[0] in SEND_COMMANDS:
            if len(commands) < 4: bot.message(data.get("sender"), f"Invalid command, please use the format: [{commands[0]} <amount> <recipient>]"); return
            recipientName = " ".join(commands[2:]).strip()
            recipientID = bot.getChar(recipientName).join().value
            if "error" in recipientID: bot.message(data.get("sender"), f"Invalid recipient [{recipientName}]. Please make sure you are also using the full name of the recipient."); return
            recipientID = recipientID.get("result").get("rid").split(".")[-1]
            try: amount = int(commands[1])
            except: bot.message(data.get("sender"), f"Invalid amount [{commands[1]}]. Please make sure you are using a valid number."); return
            if amount < 0: bot.message(data.get("sender"), f"Invalid amount [{commands[1]}]. Please make sure you are using a valid number."); return
            senderBalance = storage.get(data.get("sender").id)
            if senderBalance < amount: bot.message(data.get("sender"), f"You do not have enough money to send [{amount}] SinderBucks to [{recipientName}]."); return
            storage.set(recipientID, storage.get(recipientID) + amount)
            storage.set(data.get("sender").id, senderBalance - amount)
            bot.message(data.get("sender"), f"You have sent [{amount}] SinderBucks to [{recipientName}].")
            bot.message(recipientID, f"You have received [{amount}] SinderBucks from [{data.get('sender').name}].")
            Log(f"{data.get('sender').name} has sent [{amount}] SinderBucks to [{recipientName}].")
        elif commands[0] == "total":
            if len(commands) < 2: bot.message(data.get("sender"), f"Invalid command, please use the format: [total <password>]"); return
            if commands[1] != storage.password: bot.message(data.get("sender"), f"Invalid password [{commands[1]}]."); return
            bot.message(data.get("sender"), f"There are currently [{storage.getTotal()}] SinderBucks in circulation.")
            Log(f"{data.get('sender').name} has checked the total amount of SinderBucks in circulation.")
        elif commands[0] == "set":
            if len(commands) < 5: bot.message(data.get("sender"), f"Invalid command, please use the format: [set <recipitent> <amount> <password>")
            if ' '.join(commands[4:]) != storage.password: bot.message(data.get("sender"), f"Invalid password [{' '.join(commands[4:])}]."); return
            recipientName = " ".join(commands[1:3]).strip()
            recipientID = bot.getChar(recipientName).join().value
            if "error" in recipientID: bot.message(data.get("sender"), f"Invalid recipient [{recipientName}]. Please make sure you are also using the full name of the recipient."); return
            recipientID = recipientID.get("result").get("rid").split(".")[-1]
            try: amount = int(commands[3])
            except: bot.message(data.get("sender"), f"Invalid amount [{commands[3]}]. Please make sure you are using a valid number."); return
            # set the amount
            storage.set(recipientID, amount)
            bot.message(data.get("sender"), f"You have set [{recipientName}]'s balance to [{storage.get(recipientID)}] SinderBucks.")
            Log(f"{data.get('sender').name} has set [{recipientName}]'s balance to [{storage.get(recipientID)}] SinderBucks.")
        elif commands[0] == "get":
            if len(commands) < 4: bot.message(data.get("sender"), f"Invalid command, please use the format: [get <recipitent> <password>"); return
            if ' '.join(commands[3:]) != storage.password: bot.message(data.get("sender"), f"Invalid password [{' '.join(commands[3:])}]."); return
            recipientName = " ".join(commands[1:3]).strip()
            recipientID = bot.getChar(recipientName).join().value
            if "error" in recipientID: bot.message(data.get("sender"), f"Invalid recipient [{recipientName}]. Please make sure you are also using the full name of the recipient."); return
            recipientID = recipientID.get("result").get("rid").split(".")[-1]
            bot.message(data.get("sender"), f"[{recipientName}] has [{storage.get(recipientID)}] SinderBucks.")
            Log(f"{data.get('sender').name} has checked [{recipientName}]'s balance.")
        elif commands[0] == "multiply-all":
            if len(commands) < 3: bot.message(data.get("sender"), f"Invalid command, please use the format: [multiply-all <multiplier> <password>"); return
            if ' '.join(commands[2:]) != storage.password: bot.message(data.get("sender"), f"Invalid password [{' '.join(commands[2:])}]."); return
            try: multiplier = float(commands[1])
            except: bot.message(data.get("sender"), f"Invalid multiplier [{commands[1]}]. Please make sure you are using a valid number."); return
            storage.multiplyAll(multiplier)
            bot.message(data.get("sender"), f"You have multiplied all balances by [{multiplier}].")
        elif commands[0] in ["kill", "die", "shutdown", "stop"]:
            if len(commands) < 2: bot.message(data.get("sender"), f"Invalid command, please use the format: [{commands[0]} <password>]"); return
            if ' '.join(commands[1:]) != storage.password: bot.message(data.get("sender"), f"Invalid password [{' '.join(commands[1:])}]."); return
            bot.message(data.get("sender"), f"Shutting down...")
            bot.sleep(); bot.stop()
            Log(f"{data.get('sender').name} has shut down the bot.")
        elif commands[0] == "report":
            if len(commands) < 2: bot.message(data.get("sender"), f"Invalid command, please use the format: [report <report>]"); return
            report = ' '.join(commands[1:])
            bot.message(data.get("sender"), f"Thank you for your report, it has been sent to the developer.")
            with open("bugs.txt", "a") as f: json.dump({"report": report, "sender": data.get("sender").name, "time": time.time()}, f)
            Log(f"{data.get('sender').name} has reported the bug [{report}].")
        elif commands[0] == "suggest":
            if len(commands) < 2: bot.message(data.get("sender"), f"Invalid command, please use the format: [suggest <suggestion>]"); return
            suggestion = ' '.join(commands[1:])
            bot.message(data.get("sender"), f"Thank you for your suggestion, it has been sent to the developer.")
            with open("features.txt", "a") as f: json.dump({"suggestion": suggestion, "sender": data.get("sender").name, "time": time.time()}, f)
            Log(f"{data.get('sender').name} has suggested the feature [{suggestion}].")
        elif commands[0] == "help":
            help = f"""
**Banker Badger Commands:**

**1. main commands:**
    `message Banker = [{'/'.join(CHECK_COMMANDS)}]` - Check your character's balance.
    `message Banker = [{'/'.join(SEND_COMMANDS)}] <amount> <recipient>` - Send SinderBucks to another character.
    `message Banker = [report] <report>` - Report a bug or issue.
    `message Banker = [suggest] <suggestion>` - Suggest a feature.
    `message Banker = [help]` - Show this help message.

**2. admin commands:** 
    `message Banker = [total] <password>` - Check the total amount of SinderBucks in circulation.
    `message Banker = [set] <recipient> <amount> <password>` - Set a character's balance.
    `message Banker = [get] <recipient> <password>` - Check a character's balance.
    `message Banker = [multiply-all] <multiplier> <password>` - Multiply all character's balances by a number.
    `message Banker = [kill] <password>` - Shut down the bot.


**Example Commands:**
    `message Banker = check` - Check your character's balance.
    `message Banker = send 100 Ico Twilight` - Send 100 SinderBucks to Ico Twilight.
    `message Banker = report I can't send money to those in different areas` - Report a bug or issue.
    `message Banker = suggest I want to be able to get interest` - Suggest a feature.
    `message Banker = help` - Show this help message.


**Notes:**
    1. You must use the full name of the recipient with a space between the first and last name.
    2. You can send SinderBucks to any character, even if they are not online. But they will not receive a message saying that they have received SinderBucks unless they are online.
    3. You may use bots to send SinderBucks to other characters, but please message the developer if you do so.
    4. Ico Twilight is the developer of this bot, if you have any questions or concerns please message them on the forums.
            """
            bot.message(data.get("sender"), help)
            Log(f"{data.get('sender').name} has requested help.")
        else:
            bot.message(data.get("sender"), f"[{data.get('message')}] is an invalid command. Please type `message Banker = help` for a list of commands.")
            Log(f"{data.get('sender').name} has used an invalid command [{data.get('message')}].")

def OnBotConnect(bot):
    while(1):
        bot.ping()
        storage.save()
        time.sleep(10)

if __name__ == "__main__":
    bot = MuckletBot.Bot(TOKEN = "TOKEN", ORIGIN = "https://wolfery.com", HOST = "wss://api.wolfery.com", VERBOSE = False)
    bot.onMessage = OnBotMessage
    bot.onOpen = OnBotConnect
    bot.start()
  1. new bot library. again if intrested:
import websocket, json, threading, time, hashlib, hmac, codecs

PROMISE_TIMEOUT = "Promise Timed Out"
PROMISE_INVALID_REQUEST = "Invalid Promise Request"
PROMISE_TIMEOUT_VALUE = 10

def Error(promise, error): raise Exception(error.get("message"))

class Promise:
    def __init__(self, Manager, id): self.id = id; self.manager = Manager; self.value = None
    def join(self): self.value = self.manager.awaitResponse(self.id); return self
    def kill(self): self.manager.kill(self.id)
    def __str__(self): return f"Promise object [{self.id}:{self.manager.awaiting[self.id]}]"
    def error(self, func): func(self, self.value.get("error")) if self.value.get("error") is not None else None; return self

class PromiseManager:
    def __init__(self, ws): self.ws = ws; self.awaiting, self.itterID = {}, 0
    def __call__(self, method, params = None):
        self.itterID += 1
        id = self.itterID
        self.awaiting[id] = {'timeset': time.time(), 'response': None}
        self.send(method, id, params)
        return Promise(self, id)
    def awaitResponse(self, id):
        if self.awaiting.get(id) is None: raise Exception(PROMISE_INVALID_REQUEST)
        while self.awaiting[id]['response'] is None:
            if time.time() - self.awaiting[id]['timeset'] > PROMISE_TIMEOUT_VALUE: raise Exception(PROMISE_TIMEOUT)
        return self.awaiting[id]['response']
    def clean(self):
        for id in self.awaiting:
            if self.awaiting[id]['response'] is not None: del self.awaiting[id]
    def kill(self, id): del self.awaiting[id]
    def send(self, method, id = 0, params = None):
        request = {"id":id,"method":method,"params":params}
        msg = json.dumps(request)
        self.ws.send(msg) 
    def clear(self): self.awaiting = {}; self.itterID = 0

class Character:
    def __init__(self, bot, name = None, id = None): self.bot, self.name, self.id = bot, name, id

class Bot:
    def __init__(self, TOKEN:str = None, USERNAME:str = None, PASSWORD:str = None, BOTFIRSTNAME:str = None, BOTLASTNAME:str = None, HOST:str = 'wss://api.test.mucklet.com', ORIGIN:str = 'https://test.mucklet.com', VERBOSE:bool = False):
        self.ws = websocket.WebSocketApp(HOST,on_message = self._on_message,on_error = self._on_error,on_close = self._on_close,on_open = self._on_open)
        self.promise = PromiseManager(self.ws)
        self.TOKEN = TOKEN
        if self.TOKEN is not None:
            pass
        elif USERNAME is not None and PASSWORD is not None and BOTFIRSTNAME is not None and BOTLASTNAME is not None:
            self.BOTFIRSTNAME, self.BOTLASTNAME, self.USERNAME = BOTFIRSTNAME, BOTLASTNAME, USERNAME
            m = hashlib.sha256(); m.update(bytes(PASSWORD,'UTF-8'))
            self.PASS = codecs.encode(codecs.decode(m.hexdigest(), 'hex'), 'base64').decode()[:-1]
            self.HASH = codecs.encode(codecs.decode(hmac.new(bytes("TheStoryStartsHere",'UTF-8'),msg=bytes(PASSWORD,'UTF-8'), digestmod = hashlib.sha256).hexdigest(), 'hex'), 'base64').decode()[:-1]
        else: raise Exception("Invalid amount of login details provided. Must provide either a [token] or a [username, password, botfirstname, botlastname]")
        self.HOST, self.ORIGIN, self.VERBOSE = HOST, ORIGIN, VERBOSE
        self.onMessage, self.onError, self.onClose, self.onOpen = None, None, None, None
        self.CID, self.RID = None, None
    def start(self): self.ws.run_forever(origin=self.ORIGIN)
    def _on_message(self, ws, message):
        message = json.loads(message)
        if self.VERBOSE: print(json.dumps(message, indent=4, sort_keys=True))
        if message.get("id") is not None:
            if self.promise.awaiting.get(message.get("id")) is not None:
                self.promise.awaiting[message.get("id")]['response'] = message
                return
        if message.get("data") is None: return
        data = {
            "data": message.get("data"),
            "type": message.get("data").get("type"),
            "sender": Character(self, message.get("data").get("char").get("name") + " " + message.get("data").get("char").get("surname"), message.get("data").get("char").get("id")) if message.get("data").get("char") is not None else None,
            "reciver": Character(self, message.get("data").get("target").get("name")  + " " + message.get("data").get("char").get("surname"), message.get("data").get("target").get("id")) if message.get("data").get("target") is not None else None,
            "message": message.get("data").get("msg"),}
        if data.get("sender") is not None: 
            if data.get("sender").id == self.CID: return
        if message.get("event") is not None:
            if message.get("event").split(".")[2] != self.CID: return
        if self.onMessage is not None: 
            threading.Thread(target=self.onMessage, args=(self, data)).start()
    def _on_error(self, ws, error, message = None, code = None):print("error:", error)  
    def _on_close(self, ws, a = None, b = None):print("### closed ###", a, b)
    def _on_open(self, ws):threading.Thread(target=self.on_open, args=(ws,)).start()
    def on_open(self, ws):
        print("### open ###")
        if self.TOKEN is not None: value = self.promise("auth.auth.authenticateBot", {"token": self.TOKEN}).join().error(Error).value
        else: value = self.promise("auth.auth.login", {"name": self.USERNAME, "hash": self.HASH}).join().error(Error).value
        if self.TOKEN is not None: value = self.promise("call.core.getBot").join().error(Error).value
        else: value = self.promise("call.core.getPlayer").join().error(Error).value
        self.RID = value.get("result").get("rid")
        if self.TOKEN is not None: self.CID = self.RID.split('.')[-1]
        if self.TOKEN is None:
            value = self.promise("get."+self.RID).join().error(Error).value
            result = value.get("result"); models = result.get("models")
            for CID, data in models.items():
                if data.get("name") == self.BOTFIRSTNAME and data.get("surname") == self.BOTLASTNAME: self.CID = data.get("id")
        value = self.promise("call."+self.RID+".controlChar",params={'charId':self.CID}).join().error(lambda promise, error: None).value
        self.wake().join().error(lambda promise, error: None)
        value = self.promise("subscribe."+self.RID).join().error(Error).value
        if self.onOpen is not None: self.onOpen(self)
    def sleep(self): return self.promise("call.core.char."+self.CID+".ctrl.sleep")
    def wake(self): return self.promise("call.core.char."+self.CID+".ctrl.wakeup")
    def say(self, msg): return self.promise("call.core.char."+self.CID+".ctrl.say",params={'msg':msg})
    def pose(self, msg): return self.promise("call.core.char."+self.CID+".ctrl.pose",params={'msg':msg})
    def whisper(self, target, msg, pose='whispers'): return self.promise("call.core.char."+self.CID+".ctrl.whisper",params={'msg':msg,'charId':target.id if type(target) == Character else target,'pose':pose})
    def address(self, target, msg, pose='addreses'): return self.promise("call.core.char."+self.CID+".ctrl.address",params={'msg':msg,'charId':target.id if type(target) == Character else target,'pose':pose})
    def message(self, target, msg): return self.promise("call.core.char."+self.CID+".ctrl.message",params={'msg':msg,'charId':target.id if type(target) == Character else target})
    def ping(self): return self.promise("call.core.char."+self.CID+".ctrl.ping")
    def getChar(self, name): return self.promise("call."+self.RID+".getChar", params={"charName": name})
    def stop(self): self.ws.close()

Yes I know the code is absolutly messy as hell but I know whats going on and that’s all that matters ^w^

Reading all this makes me want to revive my old idea of running a private ethereum blockchain with a wolfery specific crypto currency (because nothing is more exciting as a horrendously overengineered distributed database running on a single machine).

1 Like

Ooh, that sound awesome. Far cooler than anything I could make ^w^. If you do it I would love to see the results. Maybe you might even use python… And maybe even a library for mucklet bots that somebody made… ^w^

I’m actually reading the ethereum docs right now and it’s so much bullshit, ffs. Why are those things so grossly overcomplicated?

The fun (is it fun? I think it’s fun) thing about blockchain is that someone wrote the actual currency mechanics 1000 times over and they even sold monkey pictures with it. You can find smart contracts for all kinds of the typical banking operations, loans, mortgages, etc. and they are pretty much just plugged into your chain. Of course if you want to interact with it from wolfery specifically (as opposed to the common wallets) you need a bot, but there’s a bunch of ethereum libraries for JS (to use with the official mucklet library) and dart (to use with my dart version).

1 Like

Yeah, it sounds fun, hope your brain don’t turn to goop though >w< good luck x

I… mmn.

While I encourage fun programming hobby projects, including those that make Wolfery a weirder place, I would ask that we please keep real-life crypto stuff uncoupled from Sinder.

2 Likes

It’d be insane to run this as a “real” crypto. I meant having a dedicated blockchain not based by anything else (because I ain’t got money to burn for those weird fees). Asked @Shinyuu and she came up with SCAM: Secure Credits and Mortgages.

I spent some time trying to figure how ethereum works yesterday and it’s insane (from the engineering point of view) how inefficient and expensive the basic stuff is in there. Even if you run your own isolated ethereum blockchain it’s still widely inefficient and it doesn’t provide anything that you couldn’t do with the traditional tooling.

That said, I totally made a coin. Apparently you can make your own coins on this thing in 5 lines of code, which might explain the hype.

image

Of course, this has zero practical use because I have 904625697166532776746648320380374280103671755200316906558 coins now, and it wouldn’t make any sense to pretend this shit is useful for exchanging into real money, its value is bloated so much more than the US dollars, and even though it’s backed by the independent organization ran by dragons (they know how to count money!) I wouldn’t put all my savings into SCAM.

… I gonna go and make loans work, now. No one knows when the coffee prices in Sinder is going to skyrocket, better be prepared.

2 Likes

Hehe, Imagine if the value crashes so much that you would need over 2000 characters to just be able to send a decent amount >w< that would be funny lol.

image

1 Like

I mean, to be honest I have an Economy System that I choose to do on my own time.

Basically, I keep tabs of my character’s finances, and just add or subtract them via an in-game “Wallet Item” my character has in his inventory, with its own separate divider.

So it goes something like this, using Skellings and Shillings as separate “Currencies” based on whichever RP Location I’m in:

[[Inventory]]
• Wallet

[[Wallet]]
• 100 Skellings
• 130 Shillings

This way, it is entirely optional, and I’m in control of whether it counts or not, without forcing it onto other people. I would simply update it as I go along in an RP, and even open up opportunities to IC’ly go to a Currency Exchange place to switch the currencies in-between.

Even if there are no Currency Exchange locations, I’d assume it would be in the metro stations realistically, so I’d be headed that way. Gives it another purpose besides introducing new players, too, and may even convince others to go along with it too! And again, it’d just be an optional side-thing, without having to be incorporated into an actual feature, so everyone wins! :wink:

2 Likes

You might be interested in registering your currency with dracones finances and availing of a premium exchange rate to sinderbucks and whatever else!

2 Likes

Or maybe even SinderBucks via banker Badger :point_right::point_left: hehe, just joking. You can do it any way that you would like ^w^
This isn’t the battle of currencies hehe x