Valhalla Legends Forums Archive | Battle.net Bot Development | Message Variables

AuthorMessageTime
Blade_360
How do I set variables for stuff like Greeting Messages and Idles?
I've tried doing just
[code]
Dim channel As String
or
Dim %channel As String
[/code]
but then it says invaild character
March 14, 2003, 6:48 PM
iago
dim channel as String

channel = "Hi, this bot sucks!"
March 14, 2003, 10:39 PM
PaiD
I think it would be like this

Dim %Chan as string

%Chan = frmmain.txtchannel.text
March 14, 2003, 11:19 PM
St0rm.iD
I don't.
March 15, 2003, 12:05 AM
Blade_360
maybe I should rephase this
it won't let me do stuff like
Dim !example As String
Dim @example As String
Dim #example As String
I want to know how you do it. I know it can be done because I've seen VB bots that can do it
March 15, 2003, 1:15 PM
Noodlez
you cant. what you probably saw was dim bleh$  that's just declaring it as a string $ = string % = integer & = long
March 15, 2003, 1:38 PM
iago
If he seen bots do it, he might be talking about commands, like:
"!ban iago[vL] You're too awesome for us"

Maybe he doesn't realize the distinction between the variable name and the contents or something.  
March 15, 2003, 2:04 PM
Blade_360
no I don't mean stuff like triggers I mean as in Idles
like

/w %username Welcome to %channel
[EDIT:]I figured it out
March 15, 2003, 7:15 PM
ILurker
lol
at the top of your source put
[code]Dim CurrentChan as string[/code]
for the onchannel event make it set that variable as the channelname
[code]CurrentChan = channelname[/code]
then for the onuser event do something like this
[code]Send "/w " & username & " " & message[/code]
March 15, 2003, 8:52 PM
warz
This might work.

[code]
'Possible usage
Private Sub Form_Load()
   Text1.Text = format_message("%greet The current time is %time, and %variable_message")
End Sub

'Replace function
Private Sub replace_variable(ByVal full_message As String, ByVal custom_variable As String, _
                            ByVal custom_alternative As String, ByRef final_message)
   final_message = Replace(full_message, custom_variable, custom_alternative)
End Sub

'Format message function
Private Function format_message(ByVal full_message As String) As String
Const variable_message = "This is the variable message that states that warz owns me!"
   Call replace_variable(full_message, "%time", Time, full_message)
   Call replace_variable(full_message, "%greet", "Greetings!", full_message)
   Call replace_variable(full_message, "%variable_message", variable_message, full_message)
   format_message = full_message
End Function
[/code]
March 15, 2003, 8:59 PM
Spht
[quote]Or, you could just use instr() and replace().
I haven't done this in awhile, but it might look something like:

[code]
if instr(1, idlemessage, variable_chan) <> 0 then
  replace(idlemessage, variable_chan, current_chan)
end if
[/code]

I will download visual basic 6 again, and produce some simple examples, with decent commentary.[/quote]

It is not necessary to use InStr() before using Replace(). If Replace() can not find the string it searched for, it will return the unchanged original string.
March 15, 2003, 9:17 PM
Atom
Dim declares your variable and sets its type.

Dim blah as string
blah = "hi"

That creates the variable named blah and puts "hi" into it.

now if you didnt dim your variable
blah = "hi"

that makes blah a variant... which takes a lot of memory.

but if you do:

blah$ = "hi"

that makes blah a string without using the dim statement.
March 16, 2003, 1:16 AM
warz
[quote]no I don't mean stuff like triggers I mean as in Idles
like

/w %username Welcome to %channel
[EDIT:]I figured it out[/quote]
he dint knowhow to explain how he wass aksing i dont think. loojks like he confused %variables n the bots like raibnot where u cant put them in the idle mesgaes and greetgs and stuff and it rpelaces them, with vaibrles in visula basic compiler,

soryr ill fix thix tomrrow   :- :-/
March 16, 2003, 1:22 AM
Camel
[quote]Dim declares your variable and sets its type.

Dim blah as string
blah = "hi"

That creates the variable named blah and puts "hi" into it.

now if you didnt dim your variable
blah = "hi"

that makes blah a variant... which takes a lot of memory.

but if you do:

blah$ = "hi"

that makes blah a string without using the dim statement.[/quote]
put "option explicit" on the first line of all your modules/forms/whatever
also go to options->tools and check require variable declaration, so that it will automaticly put option explicit at the top for you
March 16, 2003, 12:35 PM
Grok
Everyone has enough experience with bots to evolve them to a higher level of abstraction, so I submit the following.

As programmers we look for ways to write code that reduces our dependency on implementation rules.  In other words, we want to write as much abstract code as we can.  The more functions we write that do good work, yet know nothing about why they're doing it, the smaller our programs can become, and the easier it is to write the rules-specific implementation.

For example, I can write code to cook an egg (i'll get back to bots in a minute):

Dim MyEgg as New PoultryLib.Egg
Dim MyPan As New KitchenwareLib.Skillet
MyEgg.Color = Brown
MyEgg.Size = Large
MyEgg.Grade = A
MyEgg.CrackOpen MyPan.CookingSurface

That uses a pretty decent level of abstraction, because the code in the Egg class doesn't know what we're going to do with the egg.  It merely provides properties and methods for us to do egg-like things.

Bots
Bots are a mature entity in the programming world, and we should have highly abstracted their programming some time ago.  In fact, it has been done to some degree as far back on battle.net as nbbot.  Remember nbbot.cfg?  I wrote a tutorial that described how to use nbbot and categorized the abstracted segments of a line as event triggers, conditions, actions, and I forgot what I named the final part or event what it did.

&trigger&condition&action&....?

Now here we are, years later, and nearly everyone is writing tightly coupled commands for your bots.

Users of your bots should be able to name commands whatever they want, should be able to put whatever conditions they want, and define one or multiple actions that result from the conditions being met.

Why is that possible?  Because there is a finite set of events and objects exposed on battle.net  Your job as the bot programmer is to expose interfaces to things like:

channels
users
emotes
talks
whispers
infos

There are more but the list is small.  You should be programming in a generic way around those objects.  Let your users decide precisely how they will happen.  Write most of the standard usages yourself and distribute those with your bot.  Then give them an understanding of how to add rules themselves and customize as they see fit.

I'd like to see what you guys come up with.
March 16, 2003, 1:30 PM
iago
eew, brown egg?  Eggs should be white! :(

</missingthepoint>

That's spoken like a true object oriented programmer!  I've heard several lectures on exactly that in the last week from my OOP prof about abstraction and all that, and it is true.  Code should be totally reusable, people should be able to pick up a copy of your header and use it for their own satanic purposes without knowing anything whatsoever about the implementation.

I notice that as I go farther and farther into CS there is more and more seperation of definition and implementation.  I can't remember the last time I put function prototypes at the top of a file, or structured a program so it didn't need prototypes.  

Well, that's enough generalizing.  
bot.
There, not it fits in!

-iago
March 16, 2003, 3:10 PM
St0rm.iD
A standard was already proposed by me and hype, the iBScript standard.

Basically, you have the VBScript control and expose an object called Bot. This is the old documentation from PhatBot (the name was changed from PBScript to iBScript):




PhatBot Scripting Documentation
===============================

1. Adding a simple command

Adding a command in PBScript is easy. To begin a command, type:

Sub Command_word(user, text)

where word is replaced by the command word. i.e. if you wanted an !say command, the definition would be:

Sub Command_Say(user, text)

Pretty simple, huh? Remember to end your commands with End Sub when you've put all the instructions in it. Well, now we actually have to make the command do something! It's really easy. For example, if you wanted to make the bot say something, you would type:

Bot.Say "text"

OK, that's easy! Now, take a look at the example !say command in PBScript:

Sub Command_Say(user, text)
     Bot.Say user & " says " & text
End Sub

Wait a second, that Bot.Say looks funny! Yes, it has a bunch of &'s in it and strange quotation marks. user and text are variables. Variables are put outside of quotation marks. If you want to combine a variable and something you write, you use a & sign. If user was "$t0rm" and the text was "hi", the bot would say "$t0rm says hi".

2. Advanced Commands and Tricks

Well, now that you've had your !say command, we're going to make a !serve command. This may seem easy, but if you notice, we need to find certain words inside the text variable. This is not that hard to do. We just need to add a few lines of code. Here is our !serve command:

Sub Command_Serve(user, text)
     Dim word
     word = Split(text, " ")
     Bot.Say user & " serves " & word(0) & " an ice cold " & word(1)
End Sub

The first two lines the the command set a variable, word, to all of the words in the text. If you want to get the first word in the text, use word(0). If you want the second, use word(1), and so on. Now that we have our basic commands, we're going to add some useful ones now.

We're going to make an !kick command. This will use all of our skills and more. Here is the command:

Sub Command_Kick(user, text)
     Dim word
     word = Split(text, " ")
     If Bot.ReadFlag(user, "CanKick") = "yes" Then
           Bot.Say "/kick " & word(0) & " (kicked by " & user & ")"
           Bot.Say "/w " & user & " user " & word(0) & " has been kicked."
     Else
           Bot.Say "/w " & user & " You're not allowed to kick!"
     End If
End Sub

Whew! That was pretty complicated! The most perplexing thing in that is one of PB's most powerful features: its fully customizable database. But first, let me discuss the If-Then-Else statement. If-Then-Else makes decisions. Line 3 can be described as: If this user can kick, Then kick the person, and tell the user he kicked him. Else, tell the user he can't kick.

3. The Bot Object

The Bot object is the cornerstone of PBScript. The Bot object is responsible for communicating with the bot and telling it what to do. Here's a run down of all of its methods.

Say(text): Says something to battle.net
PrintText(text): Prints text in the chat window
SetStatus(text): Sets the status bar on the bottom of the screen
AddFlag(user, flagname, value): Sets a user's flag
ReadFlag(user, flagname): Read a user's flag
SetTimer(interval): Creates a timer with the specified interval (returns the timer index)
KillTimer(index): Kills a timer, created with SetTimer
Connected(): Returns if the bot is connected
BotName(): Returns the bot's assigned name
CurrentChannel(): Returns the channel the bot is currently in
HomeChannel(): Returns the bot's home channel
OpenBrowser(url): Opens a browser window, pointing to the specified URL
OnlineCount(): How many people are online right now
NumberInChannel(): Get the number of people in the current channel

4. Events

There are a couple events in PhatBot. Here's a list:

Command_word(user, text): Fires when a command is issued.
     ex: Sub Command_Ban(user, text) would fire for !ban.
Bnet_Connect(): Fires when we connect to battle.net
Bnet_Disconnect(): Fires when we disconnect from battle.net
Bnet_Talk(user, text): Fires when someone talks (useful for swear filters)
Bot_Timer(index): Fires when a timer with the index of index reaches its interval
Bot_Online(user): Fires when a user from your online monitor comes online
Bot_Offline(user): Fires when a user from your online monitor comes offline
March 16, 2003, 4:23 PM
St0rm.iD
Hrm, the documentation left out a lot of events...there's stuff like Bnet_Emote and things like that.

Please note that this is based on my open-ended database, that is, a user's "flags" are just key-value pairs and can hold anything you want.

I also wrote an intellisense-based editor for the scripting language so it'd be easier for everyone to write in this script.
March 16, 2003, 4:26 PM
Camel
grok, zds is a good example
not totally abstracted, close none the less
March 16, 2003, 11:22 PM
MesiaH
option explicit can be a good thing, or a bad thing. If your using it for the sole purpouse of not declaring any variable types, then option explicit will create them as variant, and convert them to there proper type when data is put into it, but it can help in error-trapping if you decided you forgot to declare a variable, or something of that nature, it has other purpouses too.

Ive read articles that prove that using symbols (!, $, &) before your variables, can actually speed up your program a little more than using the dim statement, but there is no difference really.

With that information, i think Grok's post should scure up some new creative ideas in your code creation, go for the gold!
March 17, 2003, 12:18 AM
Camel
[quote]option explicit can be a good thing, or a bad thing. If your using it for the sole purpouse of not declaring any variable types, then option explicit will create them as variant, and convert them to there proper type when data is put into it, but it can help in error-trapping if you decided you forgot to declare a variable, or something of that nature, it has other purpouses too.

Ive read articles that prove that using symbols (!, $, &) before your variables, can actually speed up your program a little more than using the dim statement, but there is no difference really.

With that information, i think Grok's post should scure up some new creative ideas in your code creation, go for the gold![/quote]

if you were trying to name advantages to not using option explicit, i must have missed them. and by the way, there is ZERO difference between "Dim MyStr As String" and "Dim $MyStr" when it comes time to compiling--the asm will look EXACTLY the same, hence that argument that it makes code faster is total bs. besides, even if it *did* make your code faster, that would still not be a reason to not use option explicit. the main reason for using option explicit is to catch typos in variable names which can lead to severe headaches and waste of time. imho, option explicit should be permanantly stuck on. if you want a variant, try "Dim MyVariant As Variant"....in fact, if you are _THAT_ lazy, you can simply type "Dim MyVariant", and drop the "As Variant"
March 17, 2003, 11:41 PM
Zakath
I think he meant using $str = "blah" (or is it str$?) as opposed to dim'ing it and then assigning. I don't know for sure though.
March 18, 2003, 1:10 PM
Camel
[quote]I think he meant using $str = "blah" (or is it str$?) as opposed to dim'ing it and then assigning. I don't know for sure though.[/quote]
uh, you still have to dim it... "Dim str$" is the same as "Dim str as string" -- in no way does the $ exempt you from declaring, and in no way does it make the program run faster. the only condition where the $ could possibly make a difference in program speed would be when option explicit is not used, and the variable is not declared. then, str$ would be implied to be a string, while str would be a variant. i don't think i need to point out why variants are slower, do i?
so, it would be totally incorrect to say that using str$ makes code execute faster. it would be more correct to say that, assuming option explicit is not used, str is [u]slower[/u], because it is a variant.
March 18, 2003, 9:08 PM
Grok
I dunno.  There might be a difference in the pcode generated, but I haven't checked.

The native code compiler probably optimizes both ways to the same code, so I'd think there'd be no difference in that case.
March 19, 2003, 7:52 AM
tA-Kane
[quote]It is not necessary to use InStr() before using Replace().[/quote]
I'm not sure about the speeds of VB since I've never used it, but in REALbasic, it's much faster (10-15x) to use InStr() instead of Replace() if the string is in fact not there. In REALbasic, Doing
[CODE]If InStr(source,search) > 0 Then
Replace(source,old,new)
End If[/CODE] saves a fair amount of processor time if the string isn't found.

For me, the tradeoff is worth it, especially when doing massive loops. Loops in REALbasic should be avoided at nearly all costs, loops are very processor intensive in REALbasic.

This may be useless to all of you, but...
[CODE]Dim i As Integer
For i = 1 To 10000
Next[/CODE]
That takes about 2 minutes on my machine, while
[CODE]int i;
for (i=0;i++;i<10000){;}[/CODE]
that takes just over 4 seconds.

[quote]Now here we are, years later, and nearly everyone is writing tightly coupled commands for your bots.

Users of your bots should be able to name commands whatever they want, should be able to put whatever conditions they want, and define one or multiple actions that result from the conditions being met.[/quote]
Speak for yourself regarding "everyone is writing tightly coupled commands".

As for that second quoted paragraph, it sounds much like what I had already planned for my commands.

Some additional things I've planned for is that the commands (or perhaps for user-friendly, the users) should be able to create stored variables on any
1) Global level (accessable by all commands on all connections owned by the bot)
2) Connection level (accessable by a all commands on a single connection)
3) Command/User level (accessable only by that command, or that user, as appropriate. Such a thing allows for the same variable name to be stored for multiple users, such as, perhaps, a password, to be checked against other commands requiring the user's password)
Those variables should be able to be stored as integers, strings, or booleans, and they should be able to be retrieved by any command.
All variables should be able to be stored between bot sessions (eg, if your bot runs, sets some variables, exits, then runs again, the variables should be loaded).

Additionally, once you have command-defined and command-retrieved variables, your command's expandability will be nearly maximized by allow commands to execute other commands, based on the define variables. So, you could have a command such as perhaps this...
[QUOTE]Listen: "!logon" %cmd_username %cmd_userpass %glbl_botpassword
Listen: "!connect" %cmd_myusername %cmd_myuserpass %glbl_botpassword
Response:[CODE]newvar returnstring string
if (glbl_privmsg) then returnstring="/msg cmd_user"
if %cmd_botpassword = (cmd_botpassword) then
 returnstring=+"Logging a new connection onto Battle.net using the account " %username "."
 Bot.CreateNewConnection(%myusername,%myuserpass)
else
 returnstring=+"Sorry, the bot password is incorrect."
end if
Response=returnstring
[/CODE]
[/QUOTE]

This example has 2 listens triggering the same response, and the response itself has multiple *possible* responses, but uses variables and some scripting to change the single response, though it would be possible to send multiple responses at a time, using multiple "Response:" statements like this...
[QUOTE]Listen: "!help"
Response: "Sorry, this bot does not offer help."
Response: "Perhaps you should check into a psychiatric ward?"
[/QUOTE]

Of course, using such a system would be fairly hard to implement, and it would probably be easier to use vbscript as your command system.

I've already worked out a way of using REALbasic's scripting for a similar thing, in a different project. It's not going to be hard to move that code to my Battle.net bot, and I plan on doing it when I stop being lazy... =P


Edit:
Ack! Wrote all that without realizing there's a second page to the thread, which already states most that I've just stated... :-[
March 19, 2003, 11:36 PM
Camel
[quote][CODE]Dim i As Integer
For i = 1 To 10000
Next[/CODE]
That takes about 2 minutes on my machine, while
[CODE]int i;
for (i=0;i++;i<10000){;}[/CODE]
that takes just over 4 seconds.[/quote]
that's because that only executes "{;}" once. try "for(i = 0; i < 10000; i++);"


oh and...seriously four seconds? even with the code working correctly, it takes less than a millisecond
in fact,  it took an average of about 310ms to do a loop until 1E+8 (100 million)!
[code]//main.c

#include <stdio.h> //printf()
#include <windows.h> //DWORD, GetTickCount()

void main()
{
     DWORD ticks;
     ticks = GetTickCount();
     for(int i = 0; i < (100 * 1000 * 1000); i++)
     {
           //do nothing!
     }
     ticks = GetTickCount() - ticks;

     printf("Time to completion: %ums", ticks);
}[/code]
outputs: Time to completion: 312
March 21, 2003, 2:45 PM
St0rm.iD
Shouldn't the compiler optimize that out?
March 23, 2003, 1:18 AM
Camel
[quote author=St0rm.iD link=board=17;threadid=507;start=15#msg6270 date=1048382318]
Shouldn't the compiler optimize that out?
[/quote]
you would think, but the fact that i WAS getting results (after making the loop _extremely_ long) seems to show that it isn't. btw, the comp i ran that on has a 1.73GHz AMD Athalon (XP 2100+) cpu.
March 24, 2003, 5:55 AM
Skywing
[quote author=Camel link=board=17;threadid=507;start=15#msg6316 date=1048485300]
[quote author=St0rm.iD link=board=17;threadid=507;start=15#msg6270 date=1048382318]
Shouldn't the compiler optimize that out?
[/quote]
you would think, but the fact that i WAS getting results (after making the loop _extremely_ long) seems to show that it isn't. btw, the comp i ran that on has a 1.73GHz AMD Athalon (XP 2100+) cpu.
[/quote]Are you sure those weren't due to random chance? To get a good indication you'll probably need to boost the thread priority a bit and make sure nothing else processor-intensive is running.
March 24, 2003, 8:30 PM
Camel
[quote author=Skywing link=board=17;threadid=507;start=15#msg6335 date=1048537859]
[quote author=Camel link=board=17;threadid=507;start=15#msg6316 date=1048485300]
[quote author=St0rm.iD link=board=17;threadid=507;start=15#msg6270 date=1048382318]
Shouldn't the compiler optimize that out?
[/quote]
you would think, but the fact that i WAS getting results (after making the loop _extremely_ long) seems to show that it isn't. btw, the comp i ran that on has a 1.73GHz AMD Athalon (XP 2100+) cpu.
[/quote]Are you sure those weren't due to random chance? To get a good indication you'll probably need to boost the thread priority a bit and make sure nothing else processor-intensive is running.
[/quote]
yeah i'm sure. when i upped the loop by a factor of 10, the time it took also went up that much.
March 24, 2003, 8:44 PM
CupHead
Anyway... Getting back to the subject at hand. What Warblade wants is to create a set of multi-purpose aliases, much like the aliases/variables supported in ? Bot. From the documentation:

[quote]
Aliases and variables are fun things, sometimes helpful. Anyway, a brief explanation. When you open the Variables tab on the Options dialog, you might not see anything. That's normal. To use them, add the alias or variable and then a space and then what you want it to represent. For example:

/vL /join Clan [vL]

Then click Save. Now when you type /vL in the send textbox, it will be replaced with /join Clan [vL]. For variables:

%blah% I'm so freaking bored!

Similarly, when you type %blah% in the send textbox, it is replaced with I'm so freaking bored!. They're useful for shortening things. Note the %s around the variable name. These are not necessary, but they help identify variables and you're unlikely to ever type %blah% without wanting it to be replaced. Hell, if you're like me and want to "lol" but don't want to ruin the appearance of perfect typing, add this:

lol [me=CupHead]laughs out loud.[/me]

Hope you get the idea.

Also, ? Bot supports a few internal variables.

%mp3% - The MP3 currently playing, including it's position on your playlist and the position in the song.
%channel% - The channel the bot is currently in.
%server% - The server the bot is currently connected to.
%ver% - The version of the bot.
[/quote]

Yeah, so back to how that's implemented. My bot has a queue and as it sends, variables are replaced with actual values. For example:

[code]
Private Function ReadFromQueue() As String
ReadFromQueue = ReplaceStuff(Queue.Read)
Send ReadFromQueue
End Function

Private Function ReplaceStuff(ByVal TheString as String) as String
TheString = Replace(TheString, "%mp3%", WinAmp.GetSongTitle)
TheString = Replace(TheString, "%channel%", CurrentChannel)
TheString = Replace(TheString, "%server%", CleanSlateBot1.Server)
ReplaceStuff = TheString
End Function
[/code]

Anyway, that should about cover how it's done. Note that this is very similar to what warz posted as well.
March 26, 2003, 3:56 AM

Search