VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "clsXDC"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
'////////////////////////////////////////////////////////////////////////////////////////
'// MirageBot eXtension Development Class (XDC)
'//
'// This class basically mirrors the signatures of several important MirageBot functions
'// for the purpose of allowing your scripts and plugins to interact with the application
'// and the profiles within the application.
'//
'// If you would like to see more features here please post your ideas on: www.bnetdev.net
'////////////////////////////////////////////////////////////////////////////////////////

'// Ideas:
'// Friend Functions
'// IsFriend
'// IsFriendOnline
'// Concat Collection (similar to Join Array)


'// Index of the current Bot Object
Public Index As Integer

'// Internally used variable:
'// - True if this class is being used by a Plugin
'// - False if it is being used by a Script
Public PluginClass As Boolean

'// Library Declarations
Private Declare Function URLDownloadToFile Lib "urlmon" _
    Alias "URLDownloadToFileA" (ByVal pCaller As Long, _
    ByVal szURL As String, ByVal szFileName As String, _
    ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long
Private Declare Function Beep Lib "kernel32" (ByVal dwFreq As Long, ByVal dwDuration As Long) As Long

'////////////////////////////////////////////////////////////////////////////////////////
'// SECTIONS
'//  1. Plugin/Script Management
'//  2. Bot/Profile Related
'//  3. Alias Management
'//  4. Command Management
'//  5. File Management
'//  6. Frame Management
'//  7. Group Management
'//  8. Inet Control Management
'//  9. Packet Management
'// 10. Queue Management
'// 11. Socket Management
'// 12. Timer Management
'// 13. Global Functions
'////////////////////////////////////////////////////////////////////////////////////////
    
    
    
    
    
'////////////////////////////////////////////////////////////////////////////////////////
'// FORM MANAGEMENT [BETA, UNDER CONSTRUCTION]
'////////////////////////////////////////////////////////////////////////////////////////
'    '// Under Construction
'    Public Function CreateForm(Owner, Name, Text, Width, Height, Optional OtherIndex = -1) As Object
'        '// Not Required for Plugins
'        If PluginClass Then Exit Function
'        '// Create New Form
'        Dim BI As Integer
'        If OtherIndex > -1 Then BI = CInt(OtherIndex) Else BI = Index
'        Set CreateForm = New frmEmpty
'        CreateForm.FormName = CStr(Name)
'        CreateForm.Owner = CStr(Owner)
'        CreateForm.Caption = CStr(Text)
'        CreateForm.Width = CLng(Width)
'        CreateForm.Height = CLng(Height)
'        CreateForm.BotIndex = BI
'    End Function
''




'////////////////////////////////////////////////////////////////////////////////////////
'// PLUGIN/SCRIPT MANAGEMENT
'////////////////////////////////////////////////////////////////////////////////////////
    
    '// Returns 'True' if a plugin is loaded for the given bot
    Public Function IsPluginLoaded(Name, Optional OtherIndex = -1)
        IsPluginLoaded = GetBot(OtherIndex).PluginEnabled(CStr(Name))
    End Function

    '// Returns 'True' if a script is loaded for the given bot
    Public Function IsScriptLoaded(Name, Optional OtherIndex = -1)
        IsScriptLoaded = GetBot(OtherIndex).ScriptEnabled(CStr(Name))
    End Function

    '// Load a plugin for the given bot
    '// This can be used for loading plugins that interact with your plugin kind of
    '// like a client/server or sender/receiver type of system (for example).
    Public Sub LoadPlugin(Name, Optional OtherIndex = -1)
        GetBot(OtherIndex).LoadPlugin CStr(Name)
    End Sub

    '// Load a script for the given bot
    '// This can be used for loading scripts that interact with your script kind of
    '// like a client/server or sender/receiver type of system (for example).
    Public Sub LoadScript(Name, Optional OtherIndex = -1)
        modExtension.LoadScript CStr(Name), CInt(IIf(OtherIndex = -1, Index, OtherIndex))
    End Sub
    
    '// Unload a plugin for the given bot
    '// This can be used to forcefully unload your plugin or unload a plugin that
    '// causes conflicts with your plugin.
    Public Sub UnloadPlugin(Name, Optional OtherIndex = -1)
        GetBot(OtherIndex).UnloadPlugin CStr(Name)
    End Sub

    '// Unload a script for the given bot
    '// This can be used to forcefully unload your script or unload a script that
    '// causes conflicts with your script.
    Public Sub UnloadScript(Name, Optional OtherIndex = -1)
        modExtension.UnloadScript CStr(Name), CInt(IIf(OtherIndex = -1, Index, OtherIndex))
    End Sub




        
    
'////////////////////////////////////////////////////////////////////////////////////////
'// BOT/PROFILE RELATED
'////////////////////////////////////////////////////////////////////////////////////////
        
    '// AddChat routine to append text to chat window
    Public Sub AddChat(ParamArray E())
        OutputTime frmBot.rtbChat(Index)
        Dim I As Integer, C As Integer
        C = UBound(E)
        For I = 0 To C Step 2
            OutputPart frmBot.rtbChat(Index), CLng(E(I)), CStr(E(I + 1))
        Next I
        OutputPart frmBot.rtbChat(Index), 0, vbNewLine
    End Sub
    
    '// AddChat routine to append text to chat window
    Public Sub AddChat2(OtherIndex, ParamArray E())
        OutputTime frmBot.rtbChat(CInt(OtherIndex))
        Dim I As Integer, C As Integer
        C = UBound(E)
        For I = 0 To C Step 2
            OutputPart frmBot.rtbChat(CInt(OtherIndex)), CLng(E(I)), CStr(E(I + 1))
        Next I
        OutputPart frmBot.rtbChat(CInt(OtherIndex)), 0, vbNewLine
    End Sub
    
    '// Output an event using the Style parser
    '// Some of these fields may be empty depending on the EventID and what is defined in the users' style.
    Public Sub AddEvent(EventID, Optional Username, Optional Message, Optional Flags, Optional Ping, Optional Uptime, Optional Extra, Optional OtherIndex = -1)
        If OtherIndex = -1 Then
            OutputEvent frmBot.rtbChat(Index), CLng(EventID), CStr(Username), CStr(Message), CLng(Flags), CLng(Ping), CStr(Uptime), CStr(Extra)
        Else
            OutputEvent frmBot.rtbChat(OtherIndex), CLng(EventID), CStr(Username), CStr(Message), CLng(Flags), CLng(Ping), CStr(Uptime), CStr(Extra)
        End If
    End Sub
    
    '// Connects the bot. Will disconnect an already-existent connection.
    Public Sub Connect(Optional OtherIndex = -1)
        On Error GoTo hErr:
        GetBot(OtherIndex).Connect
        Exit Sub
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "Connect"
    End Sub
    
    '// Closes any current connections within the bot.
    Public Sub Disconnect(Optional OtherIndex = -1)
        On Error GoTo hErr:
        GetBot(OtherIndex).Disconnect
        Exit Sub
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "Disconnect"
    End Sub

    '// Return the Account
    Public Function GetAccount(Username, Optional OtherIndex As Integer = -1)
        GetAccount = GetBot(OtherIndex).GetAccount(CStr(Username))
    End Function

    '// Return this 'Bot Object'
    Public Function GetBot(Optional OtherIndex = -1)
        On Error GoTo hErr:
        If OtherIndex = -1 Then
            Set GetBot = frmBot.Bot(Index)
        Else
            Set GetBot = frmBot.Bot(CInt(OtherIndex))
        End If
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "GetBot"
    End Function
    
    '// Return 'Bot Object' count
    Public Function GetBotCount()
        On Error GoTo hErr:
        GetBotCount = frmBot.Bot.UBound
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "GetBot"
    End Function
    
    '// Return this bot's name
    Public Function GetBotName(Optional OtherIndex = -1)
        On Error GoTo hErr:
        GetBotName = GetBot(OtherIndex).Profile
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "GetBotName"
    End Function
    
    '// Return YOUR clan rank
    Public Function GetClanRank(Optional OtherIndex = -1)
        On Error Resume Next
        GetClanRank = GetBot(OtherIndex).Clan.Rank
    End Function
    
    '// Return YOUR clan tag
    Public Function GetClanTag(Optional OtherIndex = -1)
        On Error Resume Next
        GetClanRank = GetBot(OtherIndex).Clan.Tag
    End Function
    
    '// Return a clan member object
    Public Function GetMember(Username, Optional OtherIndex = -1)
        'Member Object Contains:
        ' - Username    As String       // clan members name
        ' - Rank        As Byte         // 0-4 value (0 - initiate, 1 - peon, 2 - grunt, 3 - shaman, 4 - chieftain)
        ' - Online      As Byte         // 0 -- offline, 1 -- online
        ' - Location    As String       // present when location of a clan member is known
        On Error GoTo hErr:
        Dim ui As Integer
        ui = GetBot.Clan.Find(CStr(Username))
        If ui > -1 Then Set GetMember = GetBot.Clan.GetByIndex(ui)
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "GetMember"
    End Function
    
    '// Return a clan member object by index in array
    Public Function GetMemberByIndex(Index, Optional OtherIndex = -1)
        'Member Object Contains:
        ' - Username    As String       // clan members name
        ' - Rank        As Byte         // 0-4 value (0 - initiate, 1 - peon, 2 - grunt, 3 - shaman, 4 - chieftain)
        ' - Online      As Byte         // 0 -- offline, 1 -- online
        ' - Location    As String       // present when location of a clan member is known
        On Error GoTo hErr:
        Set GetMemberByIndex = GetBot(OtherIndex).Clan.GetByIndex(CInt(Index))
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "GetMemberByIndex"
    End Function
    
    '// Return clan member count
    Public Function GetMemberCount(Optional OtherIndex = -1)
        On Error GoTo hErr:
        GetMemberCount = GetBot(OtherIndex).Clan.Count
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "GetMemberCount"
    End Function
    
    '// Return a clan members location
    Public Function GetMemberLocation(Username, Optional OtherIndex = -1)
        On Error Resume Next
        GetMemberLocation = GetMember(Username, OtherIndex).Location
    End Function
    
    '// Return a clan members online status
    Public Function GetMemberOnline(Username, Optional OtherIndex = -1)
        On Error Resume Next
        GetMemberOnline = GetMember(Username, OtherIndex).Online = 1
    End Function
    
    '// Return a clan members rank
    Public Function GetMemberRank(Username, Optional OtherIndex = -1)
        On Error Resume Next
        GetMemberRank = GetMember(Username, OtherIndex).Rank
    End Function
    
    '// Return 'Options' Object
    Public Function GetOptions()
        Set GetOptions = options
    End Function
    
    '// Return the rank's alternative name (usually a letter)
    Public Function GetRankAlias(RankName)
        On Error Resume Next
        GetRankAlias = GetRanking(CStr(RankName)).Alias
    End Function
    
    '// Get an array list containing the Ranks this user is capable of Managing
    '// This means they are able to /add, /remove users with any one of these array items
    '// These are from the 'manages' field in the Rank Editor.
    Public Function GetRankManages(RankName) As String()
        On Error Resume Next
        GetRankManages = GetRanking(CStr(RankName)).Manages
    End Function
    
    '// Get an array list containing the Ranks this user Inherits
    '// These are from the 'inherits' field in the Rank Editor.
    Public Function GetRankInherits(RankName) As String()
        On Error Resume Next
        GetRankInherits = GetRanking(CStr(RankName)).Inherits
    End Function
    
    '// Return selected 'Bot' Index
    Public Function GetSelected()
        On Error GoTo hErr:
        GetSelected = SelBot
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "GetSelected"
    End Function
    
    '// Get server Lordaeron, USWest, etc.. for specific server IP address
    Public Function GetServerSuffix(Server, IsWarCraft3 As Boolean)
        GetServerSuffix = GetServerName(CStr(Server), CBool(IsWarCraft3))
    End Function
    
    '// Return a user object
    Public Function GetUser(Username, Optional OtherIndex = -1)
        'User Object Contains:
        ' - Username           As String    // username (including prefixes/suffixes)
        ' - Flags              As Long      // users flags
        ' - Statstring         As String    // unparsed statstring
        ' - Ping               As Double    // ping value at login
        ' - Client             As String    // 4-character product identifier
        ' - Expansion          As Boolean   // true when D2XP or W3XP (cdkey hashing feature)
        ' - Character          As String    // character name is IsCharacter = true
        ' - Account            As String    // username (no prefixes/suffixes)
        ' - TickJoin           As Double    // time this user has been seen for
        ' - TickTalk           As Double    // last time this user talked
        ' - TickWhisper        As Double    // last time this user whispered you
        ' - IsRepresentative   As Boolean   // true when the user is a representative
        ' - IsOperator         As Boolean   // true when the user is an operator
        ' - IsSpeaker          As Boolean   // true when the user is a speaker
        ' - IsAdministrator    As Boolean   // true when the user is an administrator
        ' - IsPlug             As Boolean   // true when a user has a UDP plug
        ' - IsIgnored          As Boolean   // true when a user is ignored
        ' - IsCharacter        As Boolean   // true when a user has a character prefix on their name
        ' - IsBannable         As Boolean   // true when a user can be banned
        ' - IsInvisible        As Boolean   // true when user was discovered on FlagUpdate event
        ' - IsShown            As Boolean   // false when user is not yet added to listview (flood protect feature)
        ' - IsIdle             As Boolean   // recently idle (light gray in channel)
        ' - IsIdler            As Boolean   // mid-range idle time (gray in channel)
        ' - IsIdlest           As Boolean   // highest idle time (dark gray in channel)
        On Error GoTo hErr:
        Dim ui As Integer
        ui = GetBot(OtherIndex).Users.Find(CStr(Username))
        If ui > -1 Then Set GetUser = GetBot(OtherIndex).Users.GetByIndex(ui)
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "GetUser"
    End Function
    
    '// Return a user object by index
    Public Function GetUserByIndex(Index, Optional OtherIndex = -1)
        'User Object Contains:
        ' - Username           As String    // username (including prefixes/suffixes)
        ' - Flags              As Long      // users flags
        ' - Statstring         As String    // unparsed statstring
        ' - Ping               As Double    // ping value at login
        ' - Client             As String    // 4-character product identifier
        ' - Expansion          As Boolean   // true when D2XP or W3XP (cdkey hashing feature)
        ' - Character          As String    // character name is IsCharacter = true
        ' - Account            As String    // username (no prefixes/suffixes)
        ' - TickJoin           As Double    // time this user has been seen for
        ' - TickTalk           As Double    // last time this user talked
        ' - TickWhisper        As Double    // last time this user whispered you
        ' - IsRepresentative   As Boolean   // true when the user is a representative
        ' - IsOperator         As Boolean   // true when the user is an operator
        ' - IsSpeaker          As Boolean   // true when the user is a speaker
        ' - IsAdministrator    As Boolean   // true when the user is an administrator
        ' - IsPlug             As Boolean   // true when a user has a UDP plug
        ' - IsIgnored          As Boolean   // true when a user is ignored
        ' - IsCharacter        As Boolean   // true when a user has a character prefix on their name
        ' - IsBannable         As Boolean   // true when a user can be banned
        ' - IsInvisible        As Boolean   // true when user was discovered on FlagUpdate event
        ' - IsShown            As Boolean   // false when user is not yet added to listview (flood protect feature)
        ' - IsIdle             As Boolean   // recently idle (light gray in channel)
        ' - IsIdler            As Boolean   // mid-range idle time (gray in channel)
        ' - IsIdlest           As Boolean   // highest idle time (dark gray in channel)
        On Error GoTo hErr:
        Set GetUserByIndex = GetBot(OtherIndex).Users.GetByIndex(CInt(Index))
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "GetUserByIndex"
    End Function
    
    '// Return count of users in channel
    Public Function GetUserCount(Optional OtherIndex = -1)
        On Error Resume Next
        GetUserCount = GetBot(OtherIndex).Users.Count
    End Function
    
    '// Return a users cached flags
    Public Function GetUserFlags(Username, Optional OtherIndex = -1)
        On Error Resume Next
        GetUserFlags = GetUser(Username, OtherIndex).Flags
        If Err Then GetUserFlags = -2
    End Function
    
    '// Return a users cached ping
    Public Function GetUserPing(Username, Optional OtherIndex = -1)
        On Error Resume Next
        GetUserPing = GetUser(Username, OtherIndex).Ping
        If Err Then GetUserPing = -2
    End Function
    
    '// Return a users rank
    Public Function GetUserRank(Username, Optional OtherIndex = -1)
        On Error GoTo hErr:
        If OtherIndex = -1 Then
            GetUserRank = GetAccess2(CStr(Username), Index).RankName
        Else
            GetUserRank = GetAccess2(CStr(Username), CInt(OtherIndex)).RankName
        End If
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "GetUserRank"
    End Function
    
    '// Returns true when you are in a clan
    Public Function IsClanned(Optional OtherIndex = -1)
        On Error Resume Next
        IsClanned = GetBot(OtherIndex).Clan.InClan
    End Function
    
    '// Return true if user is in a FLAGGED rank
    Public Function IsFlagged(Username, Optional OtherIndex = -1)
        On Error Resume Next
        IsFlagged = GetRanking(GetUserRank(CStr(Username), OtherIndex)).Flagged
    End Function
    
    '// Return whether clan member exists
    Public Function IsMember(Username, Optional OtherIndex = -1)
        On Error Resume Next
        IsMember = (GetBot(OtherIndex).Clan.Find(Username) > -1)
    End Function
    
    '// Returns whether a user's name include a @Lordaeron, @USWest, etc.. suffix
    Public Function IsOnGateway(Username)
        IsOnGateway = modDataFormatting.IsOnGateway(CStr(Username))
    End Function

    '// Returns true when you are connected to Battle.net
    Public Function IsOnline(Optional OtherIndex = -1)
        On Error Resume Next
        IsOnline = GetBot(OtherIndex).IsOnline
    End Function
    
    '// Returns true when you are able to moderate other users
    Public Function IsOps(Optional OtherIndex = -1)
        On Error Resume Next
        IsOps = GetBot(OtherIndex).Self.IsOperator
    End Function
    
    '// Returns True if Rank exists
    Public Function IsRank(RankName)
        IsRank = RankingExists(CStr(RankName))
    End Function
    
    '// Return true if user is in a SAFE rank
    Public Function IsSafe(Username, Optional OtherIndex = -1)
        On Error Resume Next
        If OtherIndex = -1 Then
            IsSafe = GetAccess2(CStr(Username), Index).Safe
        Else
            IsSafe = GetAccess2(CStr(Username), CInt(OtherIndex)).Safe
        End If
    End Function
    
    Public Function IsSelected(Optional OtherIndex = -1)
        On Error Resume Next
        IsSelected = GetBot(OtherIndex).IsSelected
    End Function
    
    '// Check to see if a timer exists
    Public Function IsTimer(Owner, Callback)
        On Error GoTo hErr:
        Dim t As Object
        Set t = GetTimer(Owner, Callback)
        IsTimer = (Not t Is Nothing)
        Exit Function
hErr:
        ErrorHandler Err.Number, "Timer callback not found (" & Callback & ", " & Owner & ")", 0, "XDC", "IsTimer"
    End Function
    
    '// Return whether user exists
    Public Function IsUser(Username, Optional OtherIndex = -1)
        On Error Resume Next
        IsUser = (GetBot(OtherIndex).Users.Find(Username) > -1)
    End Function
    
    '// Hide This Profile OR Specified Profile
    '// This will remove the tab for this profile from MirageBot's main form
    '// Returns -1 if profile is not loaded
    '// Returns -2 if profile is already hidden
    Public Function ProfileHide(Optional Profile)
        On Error GoTo hErr:
        Dim BI As Integer
1       If LenB(Profile) = 0 Then
2           BI = Index
        Else
3           BI = GetProfileIndex(CStr(Profile))
        End If
4       If BI = -1 Then ProfileHide = -1: Exit Function
5       If frmBot.Bot(BI).IsHidden Then ProfileHide = -2: Exit Function
6       UnloadTabOnly BI
        ProfileHide = 0
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "ProfileHide"
    End Function
    
    '// Unhide This Profile OR Specified Profile
    '// This will restore the tab for this profile to MirageBot's main form
    '// Returns -1 if profile is not loaded
    '// Returns -2 if profile is not hidden
    Public Function ProfileUnhide(Optional ProfileName)
        On Error GoTo hErr:
        'Unhide a Profile
        Dim BI As Integer
1       If LenB(ProfileName) = 0 Then
2           BI = Index
        Else
3           BI = GetProfileIndex(CStr(ProfileName))
        End If
4       If BI = -1 Then ProfileUnhide = -1: Exit Function
5       If frmBot.Bot(BI).IsHidden = False Then ProfileUnhide = -2: Exit Function
6       LoadTabOnly BI
        ProfileUnhide = 0
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "ProfileUnhide"
    End Function
        
    '// Load Specific Profile
    '// Returns -1 if profile is already loaded (use ProfileReload)
    Public Function ProfileLoad(ProfileName)
        On Error GoTo hErr:
1       Dim FSO As New FileSystemObject
2       If FSO.FolderExists(AppData & "Profiles\" & ProfileName & "\") = False Then ProfileLoad -2: Exit Function
3       If IsProfileLoaded(CStr(ProfileName)) Then ProfileLoad = -1: Exit Function
4       Call LoadProfile(CStr(ProfileName))
        ProfileLoad = 0
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "ProfileLoad"
    End Function
    
    '// Reload This Profile or Specific Profile
    '// Returns -1 if profile is not loaded
    Public Function ProfileReload(Optional Profile)
        On Error GoTo hErr:
        Dim BI As Integer, TI As Integer
1       If LenB(Profile) = 0 Then
2           BI = Index
        Else
3           BI = GetProfileIndex(CStr(Profile))
        End If
        If BI = -1 Then ProfileReload = -1: Exit Function
        Call LoadProfileValues(BI)
        ProfileReload = 0
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "ProfileReload"
    End Function
    
    '// Unload This Profile or Specific Profile
    '// Returns -1 if profile is not loaded
    Public Function ProfileUnload(Optional Profile)
        On Error GoTo hErr:
        Dim BI As Integer, TI As Integer
1       If LenB(Profile) = 0 Then
2           BI = Index
        Else
3           BI = GetProfileIndex(CStr(Profile))
        End If
4       If BI = -1 Then ProfileUnload = -1: Exit Function
5       Call UnloadProfile(BI)
        ProfileUnload = 0
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "ProfileUnload"
    End Function
    
    '// Request SID_GETADVLISTEX Response
    '// The return value of this function indicates a RequestID
    '// The response from this request will be shown in the script event Event_GameList(RequestID, Values())
    '// Condition refers to game-type and can be any one of these values:
    '// - ALL, MELEE, FFA, 1V1, CTF, GREED, SLAUGHTER, SUDDEN
    '// - LADDER, IRONMAN, UMS, TEAM MELEE, TEAM FFA, TEAM CTF, TVB
    '// By default ALL will be requested if this option is left empty.
    Public Function RequestGameList(Optional Condition, Optional OtherIndex = -1)
        Dim r As Long
        r = GetTickCount
        GetBot(OtherIndex).SendGameList CStr(Condition), r
        RequestGameList = r
    End Function

    '// Request SID_READUSERDATA Response
    '// The return value of this function indicates a RequestID
    '// The response from this request will be shown in the script event Event_UserProfile(RequestID, Keys(), Values())
    Public Function RequestUserProfile(Username)
        Dim r As Long, Keys(3) As String
        Keys(0) = "profile\age"
        Keys(1) = "profile\sex"
        Keys(2) = "profile\location"
        Keys(3) = "profile\description"
        r = GetTickCount
        GetBot.SendReadUserData CStr(Suffix(CStr(Username))), Keys, False, r, False
        RequestUserProfile = r
    End Function
    
    Public Function RequestUserProfileEx(Username, Client)
        Dim Keys() As String, p As String, r As Long
        Select Case UCase$(Client)
        Case "STAR", "SEXP", "W2BN", "D2DV", "D2XP", "WAR3", "W3XP", _
            "DRTL", "DSHR", "SSHR", "JSTR"
            p = UCase$(Client)
        End Select
        If LenB(p) = 0 Then
            Dim U As objUser
            Set U = GetBot.Users.GetByName(Username)
            If Not U Is Nothing Then
                p = U.Client
            Else
                p = GetBot.Self.ProductID
            End If
        End If
        Select Case p
        Case "D2DV", "D2XP", "DRTL", "DSHR", "CHAT"
            ReDim Keys(3)
        Case "WAR3", "W3XP"
            If Not GetBot.Self.IsWarCraft3 Then
                ReDim Keys(15)
            Else
                ReDim Keys(3)
            End If
        Case "W2BN"
            ReDim Keys(23)
        Case Else
            ReDim Keys(15)
        End Select
        Keys(0) = "profile\age"
        Keys(1) = "profile\sex"
        Keys(2) = "profile\location"
        Keys(3) = "profile\description"
        If UBound(Keys) > 3 Then
            Keys(4) = "record\" & p & "\0\wins"
            Keys(5) = "record\" & p & "\0\losses"
            Keys(6) = "record\" & p & "\0\disconnects"
            Keys(7) = "record\" & p & "\0\last game"
            Keys(8) = "record\" & p & "\0\last game result"
            Keys(9) = "record\" & p & "\1\wins"
            Keys(10) = "record\" & p & "\1\losses"
            Keys(11) = "record\" & p & "\1\disconnects"
            Keys(12) = "record\" & p & "\1\last game result"
            Keys(13) = "record\" & p & "\1\rating"
            Keys(14) = "record\" & p & "\1\high rating"
            Keys(15) = "DynKey\" & p & "\1\rank"
            If UBound(Keys) > 15 Then
                Keys(16) = "record\" & p & "\3\wins"
                Keys(17) = "record\" & p & "\3\losses"
                Keys(18) = "record\" & p & "\3\disconnects"
                Keys(19) = "record\" & p & "\3\last game"
                Keys(20) = "record\" & p & "\3\last game result"
                Keys(21) = "record\" & p & "\3\rating"
                Keys(22) = "record\" & p & "\3\high rating"
                Keys(23) = "DynKey\" & p & "\3\rank"
            End If
        End If
        r = GetTickCount
        GetBot.SendReadUserData CStr(Suffix(CStr(Username))), Keys, False, r, True, PluginClass
        RequestUserProfileEx = r
    End Function
    
    '// Add message via command handler to Queue
    Public Sub Send(Message, Optional OtherIndex = -1)
        On Error Resume Next
        GetBot(OtherIndex).SendText CStr(Message)
    End Sub
    
    '// Add message to Queue directly (skip command handler)
    Public Sub Send2(Message, Optional OtherIndex = -1)
        On Error Resume Next
        GetBot(OtherIndex).SendAway CStr(Message)
    End Sub
    
    '// Send a Battle.net packet (skips the outgoing packet handlers)
    Public Sub SendPacket(PacketID, PacketData, Optional OtherIndex = -1)
        On Error Resume Next
        GetBot(OtherIndex).SendBnetPacketData PacketID, PacketData
    End Sub
    
    '// Remove current item from the Queue
    Public Sub VetoThisMessage(Optional OtherIndex = -1)
        GetBot(OtherIndex).VetoQueuedItem
    End Sub
    
    '// Write Profile Keys (2D Array)
    '//   Write all changeable keys at once.
    Public Sub WriteUserProfile(Age, Sex, Location, Description, Optional OtherIndex = -1)
        On Error Resume Next
        Dim Keys(3, 1) As String
        Keys(0, 0) = "profile\age"
        Keys(0, 1) = CStr(Age)
        Keys(1, 0) = "profile\sex"
        Keys(1, 1) = CStr(Sex)
        Keys(2, 0) = "profile\location"
        Keys(2, 1) = CStr(Location)
        Keys(3, 0) = "profile\description"
        Keys(3, 1) = CStr(Description)
        GetBot(OtherIndex).SendWriteUserData Keys
    End Sub
    
    '// Write Profile Keys Extended (2D Array)
    '//   You may write as many keys are you need to change
    '//   Item (,0) = Key
    '//   Item (,1) = Value
    '// Change just location:
    '//   Dim Keys(0, 1) As String
    '//   Keys(0, 0) = "profile\location"
    '//   Keys(0, 1) = "Australia"
    '// Change all keys:
    '//   Dim Keys(3, 1) As String
    '//   Keys(0, 0) = "profile\age"
    '//   Keys(0, 1) = "18"
    '//   Keys(1, 0) = "profile\sex"
    '//   Keys(1, 1) = "Male"
    '//   Keys(2, 0) = "profile\location"
    '//   Keys(2, 1) = "Australia"
    '//   Keys(3, 0) = "profile\description"
    '//   Keys(3, 1) = "MirageBot"
    Public Sub WriteUserProfileEx(Keys(), Optional OtherIndex = -1)
        On Error Resume Next: GetBot(OtherIndex).SendWriteUserData Keys
    End Sub
    
    
    
    
'////////////////////////////////////////////////////////////////////////////////////////
'// ALIAS MANAGEMENT
'////////////////////////////////////////////////////////////////////////////////////////

    '// Add an Alias for specific Username on Server
    Public Function CreateAlias(Username, TAlias, Optional Server, Optional OtherIndex As Integer = -1)
         If IsOnGateway(CStr(Username)) Then
            Server = Mid$(Username, InStrRev(Username, "@") + 1)
        Else
            If Len(Server) = 0 Then Server = GetBot(OtherIndex).Config.ServerName
        End If
        CreateAlias = Alias.AddAlias(CStr(Username), CStr(TAlias), CStr(Server))
    End Function

    '// Remove an Alias for specific Username on Server
    Public Function DeleteAlias(Username, Optional Server, Optional OtherIndex As Integer = -1)
         If IsOnGateway(CStr(Username)) Then
            Server = Mid$(Username, InStrRev(Username, "@") + 1)
        Else
            If Len(Server) = 0 Then Server = GetBot(OtherIndex).Config.ServerName
        End If
        DeleteAlias = Alias.RemoveAlias(CStr(Username), CStr(Server))
    End Function

    '// Return a Users' Alias
    Public Function GetAlias(Username, Optional Server, Optional OtherIndex As Integer = -1)
         If IsOnGateway(CStr(Username)) Then
            Server = Mid$(Username, InStrRev(Username, "@") + 1)
        Else
            If Len(Server) = 0 Then Server = GetBot(OtherIndex).Config.ServerName
        End If
        GetAlias = Alias.GetAlias(CStr(Username), CStr(Server))
    End Function
    
    
    
    
'////////////////////////////////////////////////////////////////////////////////////////
'// COMMAND MANAGEMENT
'////////////////////////////////////////////////////////////////////////////////////////

    '// Create a new command
    '// Returns 'Nothing' if command already exists
        '// Owner is the exact filename of the script without the extension
        '// Callback is the function to call within your script when the command is triggered
        '// Force will forcefully add the command
        '// Use may be: 0 - Both, 1 - Internal, 2 - External
    Public Function CreateCommand(Command, Owner, Callback, _
        Optional Alias, Optional Params, Optional Help, _
        Optional Use = 0, Optional Force = False, Optional OtherIndex = -1)
        On Error GoTo hErr:
1       If CBool(Force) Then Call GetBot(OtherIndex).Commands.DeleteCommand(CStr(Command))
        Dim CC As objCmd
2       Set CC = GetBot(OtherIndex).Commands.CreateCommand(CStr(Command), CInt(IIf(PluginClass, 2, 1)))
3       If Not CC Is Nothing Then
4           With CC
5               .Alias = CStr(Alias)
6               .Parameters = CStr(Params)
8               .Help = CStr(Help)
9               .Use = CInt(Use)
10              .Owner = CStr(Owner)
11              .Callback = CStr(Callback)
                .CallbackIndex = Index
            End With
        End If
12      Set CreateCommand = CC
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "CreateCommand"
    End Function
    
    '// Delete a command
    '// Returns 'True' if successful
    Public Function DeleteCommand(Command, Optional OtherIndex = -1)
        DeleteCommand = GetBot(OtherIndex).Commands.DeleteCommand(CStr(Command))
    End Function
    
    '// Return object for a command
    '// Returns 'Nothing' if command does not exist
    Public Function OpenCommand(Command, Optional OtherIndex = -1)
        Set OpenCommand = GetBot(OtherIndex).Commands.OpenCommand(CStr(Command))
    End Function
    
    
    
    
'////////////////////////////////////////////////////////////////////////////////////////
'// FILE MANAGEMENT
'////////////////////////////////////////////////////////////////////////////////////////
    
    '// Return App.Path
    Public Function GetAppPath()
        GetAppPath = App.Path & "\"
    End Function
        
    '// Return this bot's path
    Public Function GetBotPath(Optional OtherIndex = -1)
        On Error GoTo hErr:
        GetBotPath = GetBot(OtherIndex).ProfilePath
        GetBotPath = Left$(GetBotPath, InStrRev(GetBotPath, "\"))
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "GetBotPath"
    End Function

    '// Return AppData\Data\Other Path
    Public Function GetDataPath()
        GetDataPath = AppData & "Data\Other\"
    End Function
    
    '// Returns an array of files in the given mask.
    '// Mask may be something like:
    '//  C:\Windows\*.dll - to list all dll's in Windows folder
    Public Function GetFileList(Mask)
        GetFileList = modFileIO.FileList(CStr(Mask))
    End Function
    
    '// Return AppData Path
    Public Function GetFilePath()
        GetFilePath = AppData
    End Function
        
    '// Returns an array of folders in the given directory.
    Public Function GetFolderList(Dir)
        Dim FL As Folders, GFL() As String, I As Integer
        Set FL = modFileIO.FolderList(CStr(Dir))
        ReDim GFL(FL.Count - 1)
        For I = 0 To UBound(GFL)
            GFL(I) = FL(I + 1).Name
        Next
        GetFolderList = GFL
    End Function
        
    '// Read Content from File
    Public Function LoadFile(File)
        LoadFile = modFileIO.ReadFile(CStr(File))
    End Function

    '// Download a file to the %APPDATA%\Data\Other directory
    Public Sub PrintURLToFile(sFileName, sURL)
        sFileName = GetDataPath & sFileName
        URLDownloadToFile 0, CStr(sURL), CStr(sFileName), 0, 0
    End Sub

    '//Return Config.ini Value
    Public Function ReadConfig(Section, Key, Optional OtherIndex = -1)
        On Error Resume Next: ReadConfig = modFileIO.ReadINI(GetBotPath(OtherIndex) & "Config.ini", CStr(Section), CStr(Key))
    End Function
    
    '// Return Settings.ini Value
    Public Function ReadSetting(Section, Key)
        On Error Resume Next: ReadSetting = modFileIO.ReadINI(AppSettings, CStr(Section), CStr(Key))
    End Function
    
    '// Return INI Value
    Public Function ReadINI(File, Section, Key)
        On Error Resume Next: ReadINI = modFileIO.ReadINI(CStr(File), CStr(Section), CStr(Key))
    End Function
    
    '// Save Content to File
    Public Sub SaveFile(File, Content)
        modFileIO.SaveFile CStr(File), CStr(Content)
    End Sub
    
    '// Write Config.ini Value
    Public Sub WriteConfig(Section, Key, Value, Optional OtherIndex = -1)
        On Error Resume Next: modFileIO.WriteINI GetBotPath(OtherIndex) & "Config.ini", CStr(Section), CStr(Key), CStr(Value)
    End Sub
    
    '// Write Settings.ini Value
    Public Sub WriteSetting(Section, Key, Value)
        On Error Resume Next: modFileIO.WriteINI AppSettings, CStr(Section), CStr(Key), CStr(Value)
    End Sub

    '// Write INI Value
    Public Sub WriteINI(File, Section, Key, Value)
        On Error Resume Next: modFileIO.WriteINI CStr(File), CStr(Section), CStr(Key), CStr(Value)
    End Sub
    
    
    
    
    
    
'////////////////////////////////////////////////////////////////////////////////////////
'// FRAME MANAGEMENT [BETA]
'// Frames allow you to create your own RichTextBox and ListView in the main form which
'// can then allow you to add an entirely new protocol for interaction with MirageBot or
'// even display specific information regarding a particular type of request.
'////////////////////////////////////////////////////////////////////////////////////////
    '// Create a new Frame, returns FrameObject or Nothing (if Frame already exists)
    '// - Name is the internal name which callbacks will use in the form of
    '//   Name_Object_Event (e.g. ExampleFrame_List_DblClick)
    '// - Owner is the script or plugin which owns this frame
    '// - Title is the name that will appear in the drop down combobox
    '// Name and Title MUST be UNIQUE for each script/plugin
    Public Function CreateFrame(Name, Owner, Title, Optional OtherIndex = -1)
        'If Not BETA Then Exit Function
        Set CreateFrame = GetBot(OtherIndex).Frames.CreateFrame(CStr(Name), CStr(Owner), CStr(Title), PluginClass)
    End Function
    
    '// Delete an existing Frame, returns True if successful
    Public Function DeleteFrame(Name, Owner, Optional OtherIndex = -1)
        'If Not BETA Then Exit Function
        DeleteFrame = GetBot(OtherIndex).Frames.DeleteFrame(CStr(Name), CStr(Owner))
    End Function
    
    '// Open an existing Frame, returns FrameObject or Nothing (if Frame does not exist)
    Public Function OpenFrame(Name, Owner, Optional OtherIndex = -1)
        'If Not BETA Then Exit Function
        Set OpenFrame = GetBot(OtherIndex).Frames.OpenFrame(CStr(Name), CStr(Owner))
    End Function
    
    '// Forcefully select a specific Frame for the current bot index
    Public Function SelectFrame(Name, Owner, Optional OtherIndex = -1)
        'If Not BETA Then Exit Function
        Dim Obj As objFrame
        Set Obj = OpenFrame(Name, Owner, OtherIndex)
        If Not Obj Is Nothing Then
            Dim I As Integer
            For I = 0 To frmBot.cboList.ListCount - 1
                If LCase(frmBot.cboList.List(I)) = LCase(Obj.Title) Then
                    frmBot.cboList.ListIndex = I
                    frmBot.cboList_Click
                    Exit For
                End If
            Next
        End If
    End Function
        
    
    
    
    
'////////////////////////////////////////////////////////////////////////////////////////
'// GROUP MANAGEMENT
'////////////////////////////////////////////////////////////////////////////////////////
        
    '// Add a user to an existing Group
    Public Function AddToGroup(GroupName, Username)
        AddToGroup = modGroups.AddToGroup(CStr(GroupName), CStr(Username))
    End Function
    
    '// Create a New Group
    Public Function CreateGroup(GroupName, Color)
        CreateGroup = modGroups.CreateGroup(CStr(GroupName), CLng(Color))
    End Function
    
    '// Delete an existing Group
    Public Function DeleteGroup(GroupName)
        DeleteGroup = modGroups.DeleteGroup(CStr(GroupName))
    End Function
    
    '// Remove a user from any Groups they may already belong to
    Public Function RemoveFromGroups(Username)
        RemoveFromGroups = modGroups.RemoveFromGroup(CStr(Username))
    End Function
    
    '// Rename an existing Group
    Public Function RenameGroup(OldGroupName, NewGroupName)
        RenameGroup = modGroups.RenameGroup(CStr(OldGroupName), CStr(NewGroupName))
    End Function
    
    '// Return a Group Object
    '// The object contains: Add/Remove/MemberCount/Members/IsInGroup/SaveGroup/Clear/Color
    Public Function GetGroup(Name)
        Dim I As Integer
        For I = 0 To UBound(Groups())
            If Not Groups(I) Is Nothing Then
                If LenB(Groups(I).Name) Then
                    If LCase$(Groups(I).Name) = LCase$(Name) Then
                        Set GetGroup = Groups(I)
                        Exit Function
                    End If
                End If
            End If
        Next I
        Set GetGroup = Nothing
    End Function
    
    '// Return list of Groups
    '// You may return the list then do 'GetGroup(Name)' to return an object
    Public Function GetGroups()
        Dim I As Integer, gr() As String
        ReDim gr(0)
        For I = 0 To UBound(Groups())
            If Not Groups(I) Is Nothing Then
                If LenB(Groups(I).Name) Then
                    If LenB(gr(UBound(gr))) Then ReDim Preserve gr(UBound(gr) + 1)
                    gr(UBound(gr)) = Groups(I).Name
                End If
            End If
        Next I
        GetGroups = gr
    End Function
    
    
    
    
    
    
'////////////////////////////////////////////////////////////////////////////////////////
'// PACKET MANAGEMENT
'////////////////////////////////////////////////////////////////////////////////////////

    '// Create a new packet viewer/handler
        '// Owner is the exact filename of the script/plugin without the extension
        '// Callback is the function to call within your script when the packet is triggered
        '// Override determines whether the bot will still handle this packet
        '// If OtherIndex is not set then Index will be used instead
    Public Function CreatePacket(PacketID, Owner, Callback, Optional Outbound = False, Optional Override = False, Optional OtherIndex = -1)
        Set CreatePacket = GetBot(OtherIndex).Packets.CreatePacket(CByte(PacketID), CStr(Owner), CStr(Callback), CInt(Index), CBool(Outbound), CBool(Override), PluginClass)
    End Function
    
    '// Delete a packet using the callback
    Public Function DeletePacket(PacketID, Owner, Optional OtherIndex = -1)
        DeletePacket = GetBot(OtherIndex).Packets.DeletePacket(CByte(PacketID), CStr(Owner))
    End Function
    
    '// Return object for a packet
    '// Returns 'Nothing' if Callback is not defined
    Public Function OpenPacket(PacketID, Owner, Optional OtherIndex = -1)
        Set OpenPacket = GetBot(OtherIndex).Packets.OpenPacket(CByte(PacketID), CStr(Owner))
    End Function
    
    
    
    
'////////////////////////////////////////////////////////////////////////////////////////
'// QUEUE MANAGEMENT
'////////////////////////////////////////////////////////////////////////////////////////
    
    '// Add an item to the Queue
    Public Sub QueueAdd(Message, Optional OtherIndex = -1)
        If OtherIndex <> -1 Then
            ShareQ.QAdd CStr(Message), False, CInt(OtherIndex)
        Else
            ShareQ.QAdd CStr(Message), False, Index
        End If
    End Sub
    
    '// Clear a specific Queue
    Public Sub QueueClear(Optional OtherIndex = -1)
        If OtherIndex <> -1 Then
            ShareQ.QClear CInt(OtherIndex)
        Else
            ShareQ.QClear Index
        End If
    End Sub
    
    '// Return Queue Count for Specific Bot
    Public Function QueueCount(Optional OtherIndex = -1)
        If OtherIndex <> -1 Then
            QueueCount = ShareQ.QCount(CInt(OtherIndex))
        Else
            QueueCount = ShareQ.QCount(Index)
        End If
    End Function
    
    '// Remove an item from the Queue
    Public Sub QueueDelete(Message, Optional OtherIndex = -1)
        If OtherIndex <> -1 Then
            ShareQ.QDelete CStr(Message), CInt(OtherIndex)
        Else
            ShareQ.QDelete CStr(Message), Index
        End If
    End Sub
    
    '// Return Queued Item
    Public Function QueueItem(ItemIndex)
        QueueItem = ShareQ.Queue.Item(ItemIndex)
    End Function
    
    '// Return Queue Total Count
    Public Function QueueTotal()
        QueueTotal = ShareQ.Queue.Count
    End Function
    
    
    
    
'////////////////////////////////////////////////////////////////////////////////////////
'// SOCKETS
'////////////////////////////////////////////////////////////////////////////////////////

    '// Add a winsock control
    '// Callback events will be the same as a normal winsock control:
        '// Name_Close()
        '// Name_Connect()
        '// Name_ConnectionRequest(RequestID)
        '// Name_DataArrival(bytesTotal)
        '// Name_Error(Number, Description)
        '// Name_SendComplete()
        '// Name_SendProgress(bytesSent, bytesRemaining)
    Public Function AddSocket(Name, Owner)
        Set AddSocket = frmBot.Bot(Index).AddSocket(CStr(Name), CStr(Owner), PluginClass)
    End Function

    '// Delete a winsock control
    Public Function DeleteSocket(Name, Owner)
        DeleteSocket = frmBot.Bot(Index).DeleteSocket(CStr(Name), CStr(Owner), PluginClass)
    End Function

    '// Return a winsock control object
    Public Function OpenSocket(Name, Owner)
        Set OpenSocket = frmBot.Bot(Index).OpenSocket(CStr(Name), CStr(Owner), PluginClass)
    End Function
    
    
    
    
'////////////////////////////////////////////////////////////////////////////////////////
'// TIMERS
'////////////////////////////////////////////////////////////////////////////////////////

    '// Add a timer to execute on Callback every [Interval] milliseconds
    Public Function AddTimer(Owner, Callback, Interval, Optional Enabled = True)
        On Error GoTo hErr:
        If GetTimer(Owner, Callback) Is Nothing Then
1           Dim t As New objTimer
2           t.Callback = CStr(Callback)
3           t.Interval = Val(Interval)
4           t.Owner = CStr(Owner)
            t.IsScript = Not PluginClass
5           t.Enabled = CBool(Enabled)
6           GetBot.Timers.Add t
            AddTimer = True
        End If
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "AddTimer"
    End Function
    
    '// Remove timer
    Public Sub DeleteTimer(Owner, Callback)
        On Error GoTo hErr:
        Dim I As Integer, t As objTimer
        I = 1
1       For Each t In GetBot.Timers
2           If LCase$(t.Callback) = LCase$(CStr(Callback)) And LCase$(t.Owner) = LCase$(CStr(Owner)) Then
3               GetBot.Timers.Remove I
                Exit Sub
            End If
            I = I + 1
        Next t
        Exit Sub
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "DeleteTimer"
    End Sub
    
    '// Execute a timers Callback ahead of time
    Public Sub CallTimer(Owner, Callback)
        On Error GoTo hErr:
        Dim t As Object
1       Set t = GetTimer(Owner, Callback)
2       If Not t Is Nothing Then
            If t.IsScript Then
3               GetBot.scProcess t.Owner, t.Callback
            Else
                GetBot.plProcess t.Owner, t.Callback
            End If
        End If
        Exit Sub
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "CallTimer"
    End Sub
    
    '// Change a timers settings
    Public Sub EditTimer(Owner, Callback, Interval, Optional Enabled = True)
        On Error GoTo hErr:
        With GetTimer(Owner, Callback)
            .Interval = CDbl(Interval)
            .Enabled = CBool(Enabled)
        End With
        Exit Sub
hErr:
        ErrorHandler Err.Number, "Timer callback not found (" & Callback & ", " & Owner & ")", 0, "XDC", "EditTimer"
    End Sub
    
    '// Returns true when the timer is running
    Public Function TimerEnabled(Owner, Callback)
        On Error GoTo hErr:
        TimerEnabled = GetTimer(Owner, Callback).Enabled
        Exit Function
hErr:
        ErrorHandler Err.Number, "Timer callback not found (" & Callback & ", " & Owner & ")", 0, "XDC", "TimerEnabled"
    End Function
    
    '// Returns the timer's interval
    Public Function TimerInterval(Owner, Callback)
        On Error GoTo hErr:
        TimerInterval = GetTimer(Owner, Callback).Interval
        Exit Function
hErr:
        ErrorHandler Err.Number, "Timer callback not found (" & Callback & ", " & Owner & ")", 0, "XDC", "TimerInterval"
    End Function
    
    '// Starts an existing timer
    Public Sub StartTimer(Owner, Callback)
        On Error GoTo hErr:
        GetTimer(Owner, Callback).Enabled = True
        Exit Sub
hErr:
        ErrorHandler Err.Number, "Timer callback not found (" & Callback & ", " & Owner & ")", 0, "XDC", "StartTimer"
    End Sub
    
    '// Stops an existing timer
    Public Sub StopTimer(Owner, Callback)
        On Error GoTo hErr:
        GetTimer(Owner, Callback).Enabled = False
        Exit Sub
hErr:
        ErrorHandler Err.Number, "Timer callback not found (" & Callback & ", " & Owner & ")", 0, "XDC", "StopTimer"
    End Sub
    
    '// Return Timer Object [INTERNAL]
    Private Function GetTimer(Owner, Callback)
        On Error GoTo hErr:
        Dim t As objTimer
        For Each t In GetBot.Timers
            If LCase$(t.Callback) = LCase$(Callback) And LCase$(t.Owner) = LCase$(Owner) Then
                Set GetTimer = t
                Exit Function
            End If
        Next t
        Set GetTimer = Nothing
        Exit Function
hErr:
        ErrorHandler Err.Number, Err.Description, Erl, "XDC", "GetTimer"
    End Function
    
    
    
    
'////////////////////////////////////////////////////////////////////////////////////////
'// GLOBAL FUNCTIONS
'////////////////////////////////////////////////////////////////////////////////////////
    
    '// Executes a call to the Beep() API function
    Public Function APIBeep(lFreq, lDuration)
        APIBeep = Beep(CLng(lFreq), CLng(lDuration))
    End Function

    '// Executes a call to MirageBot's FlashWindowEx() API function
    Public Sub APIFlashWindow()
        Call FlashWindow
    End Sub

    '// Pings specified server IP
    Public Function APIPing(Server)
        APIPing = PingServer(CStr(Server))
    End Function
    
    '// Play a sound from file using the PlaySound() API call
    Public Function APIPlaySound(sFilePath, Optional lFlags = SND_FILENAME Or SND_ASYNC)
        APIPlaySound = PlaySound(CStr(sFilePath), CLng(lFlags))
    End Function

    '// Kernel32's Sleep Function
    Public Sub APISleep(dwMilliseconds)
        Call Sleep(CLng(dwMilliseconds))
    End Sub

    '// Shuts down MirageBot
    Public Sub AppClose()
        Unload frmBot
    End Sub

    '// Return current build
    Public Function AppBuild()
        AppBuild = App.Revision
    End Function
    
    '// Return version x.x or x.x BETA
    Public Function AppVersion()
        AppVersion = App.Major & "." & App.Minor & " " & IIf(BETA, "BETA", "")
    End Function
    
    '// Return App Version
    Public Function GetAppVersion()
        GetAppVersion = "MirageBot " & App.Major & "." & App.Minor & " by Chriso"
    End Function

    '// Return users clan from their statstring
    Public Function ExtractClanName(Statstring)
        ExtractClanName = modStatstring.ExtractClanName(CStr(Statstring))
    End Function
    
    '// Return clients full name using the ProductID
    Public Function ProductToStr(Product)
        ProductToStr = modStatstring.ProductToStr(CStr(Product))
    End Function
    
    '// Returns the current system uptime in milliseconds as reported by the GetTickCount() API call extended using
    '   Kernel32s QueryPerformanceCounter/QueryPerformanceFrequency API calls
    Public Function GetTickCount()
        GetTickCount = DblTickCount()
    End Function
    
    '// Function for formatting the given TickCount into Weeks, Days, Hours, Minutes, Seconds.
    '// Example Usage (Time Difference):
    '//   Dim Weeks, Days, Hours, Minutes, Seconds
    '//   FormatTick(GetTickCount() - OtherTickCount, Weeks, Days, Hours, Minutes, Seconds)
    '//   AddChat vbGreen, "Time Difference: " & Weeks & " weeks, " & Days & " days, " & Hours & " hours, " & Minutes & " minutes, " & Seconds & " seconds."
    Public Sub FormatTick(TickCount, ByRef Weeks, ByRef Days, ByRef Hours, ByRef Minutes, ByRef Seconds)
        Dim DT As DATETIME
        DT = ConvertTickCount(CDbl(TickCount))
        Weeks = DT.Weeks
        Days = DT.Days
        Hours = DT.Hours
        Minutes = DT.Minutes
        Seconds = DT.Seconds
    End Sub
    
    '// Function for formatting the given TickCount into Weeks, Days, Hours, Minutes, Seconds.
    '// Example Usage (System Uptime):
    '//   Dim Weeks, Days, Hours, Minutes, Seconds
    '//   FormatTickNow(Weeks, Days, Hours, Minutes, Seconds)
    '//   AddChat vbGreen, "System Uptime: " & Weeks & " weeks, " & Days & " days, " & Hours & " hours, " & Minutes & " minutes, " & Seconds & " seconds."
    Public Sub FormatTickNow(ByRef Weeks, ByRef Days, ByRef Hours, ByRef Minutes, ByRef Seconds)
        Dim DT As DATETIME
        DT = ConvertTickCount(DblTickCount)
        Weeks = DT.Weeks
        Days = DT.Days
        Hours = DT.Hours
        Minutes = DT.Minutes
        Seconds = DT.Seconds
    End Sub
    
    '// Pattern match two strings, e.g. strText = WarBot, strMatch = War*    Matches = (WarBot Like War*)  [True]
    Public Function Matches(strText, strMatch)
        If Len(strText) = 0 Then Exit Function
        Call PrepareCheck(CStr(strMatch))
        Matches = (LCase$(CStr(strText)) Like LCase$(CStr(strMatch)))
    End Function

    '// Output Error in Error Log/Window
    Public Function OutputErr(Owner, Proc, Desc, Num, Erl)
        ErrorHandler CInt(Num), CStr(Desc), CLng(Erl), CStr(Owner), CStr(Proc)
    End Function

    '// Show No Icon Balloon in Tray
    Public Function ShowBalloon(Title, Text, Optional Timeout = 10000)
        frmBot.tiMain.BalloonTip CStr(Text), btsNoIcon, CStr(Title), CLng(Timeout)
    End Function

    '// Show Error Balloon in Tray
    Public Function ShowErrorBalloon(Title, Text, Optional Timeout = 10000)
        frmBot.tiMain.BalloonTip CStr(Text), btsError, CStr(Title), CLng(Timeout)
    End Function

    '// Show Info Balloon in Tray
    Public Function ShowInfoBalloon(Title, Text, Optional Timeout = 10000)
        frmBot.tiMain.BalloonTip CStr(Text), btsInfo, CStr(Title), CLng(Timeout)
    End Function

    '// Show Warning Balloon in Tray
    Public Function ShowWarningBalloon(Title, Text, Optional Timeout = 10000)
        frmBot.tiMain.BalloonTip CStr(Text), btsWarning, CStr(Title), CLng(Timeout)
    End Function

    '// Remove @Lordaeron, @USWest, @USEast, etc.. from Username
    Public Function StripGateway(Username)
        StripGateway = modDataFormatting.StripGateway(Username)
    End Function

    '// Encode Text in UTF8 Format
    Public Function UTF8Encode(Text)
        UTF8Encode = modSystem.UTF8Encode(CStr(Text))
    End Function

    '// Decode Text from UTF8 Format
    Public Function UTF8Decode(Text)
        UTF8Decode = modSystem.UTF8Decode(CStr(Text))
    End Function
    
