Table of Contents

Shortnote

The code below relies on a configuration file called Claviger.config that is placed in the same directory as the Claviger executable. A sample configuration file is the following:

firstname:Claviger
lastname:Sextant
password:$1$cbde97a19118750e02f054accf10a269
grid:https://login.agni.lindenlab.com/cgi-bin/login.cgi
secret:C55OPS
master:dd372c91-16a2-4414-a8b5-ec1312637ff0

The following parameters must be configured:

Compiling the Bot

If you have downloaded the binaries, you can skip this step. It is provided here in case you want to modify or recompile Claviger.

From the program folder, provided you have mono installed and you are on a UNIX system, issue:

dmcs Claviger.cs -r:libomv/OpenMetaverse.dll -r:libomv/OpenMetaverseTypes.dll

on the command line to compile the bot.

Code

Claviger.cs
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2012 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Threading;
using System.Collections.Generic;
using OpenMetaverse;
using System.Linq;
 
namespace Claviger {
  internal class Claviger {
    private static readonly GridClient Client = new GridClient();
    private static string _firstName = string.Empty;
    private static string _lastName = string.Empty;
    private static string _password = string.Empty;
    private static List < UUID > _masters = new List < UUID > ();
    private static readonly Thread MainConnectionThread = new Thread(MainConnection);
    private static Dictionary < char, bool > StabilizeChecks = new Dictionary < char, bool > () {
      {
        'l', false
      }, {
        'e', false
      }, {
        's', false
      }, {
        'a', false
      }
    };
    private static List < string > AuthAgents = new List < string > ();
    private static UUID _sitTargetUUID;
    private static volatile bool _run = true;
 
    public static void Main() {
 
      Settings.LOG_LEVEL = Helpers.LogLevel.None;
      Client.Settings.STORE_LAND_PATCHES = true;
      Client.Settings.ALWAYS_DECODE_OBJECTS = true;
      Client.Settings.ALWAYS_REQUEST_OBJECTS = true;
      Client.Settings.SEND_AGENT_UPDATES = true;
      Client.Settings.USE_ASSET_CACHE = false;
      Client.Settings.FETCH_MISSING_INVENTORY = true;
 
      Client.Network.LoginProgress += HandleLoginProgress;
      Client.Appearance.AppearanceSet += HandleAppearanceSet;
      Client.Network.SimConnected += HandleSimulatorConnected;
      Client.Network.Disconnected += HandleDisconnected;
      Client.Network.SimDisconnected += HandleSimulatorDisconnected;
      Client.Network.EventQueueRunning += HandleEventQueueRunning;
 
      Client.Friends.FriendshipOffered += HandleFriendshipOffered;
      Client.Self.ScriptQuestion += HandleScriptQuestion;
      Client.Self.IM += HandleSelfIM;
 
      try {
        foreach(string cfg in File.ReadLines("Claviger.config")) {
          string[] data = cfg.Split(new char[] {
            ':'
          }, 2);
          switch (data[0]) {
          case "firstname":
            _firstName = data[1];
            break;
          case "lastname":
            _lastName = data[1];
            break;
          case "password":
            _password = data[1];
            break;
          case "grid":
            Client.Settings.LOGIN_SERVER = data[1];
            break;
            // auth-secrets and masters are read dynamically on request.
          case "master":
            break;
          case "secret":
            break;
          default:
            Console.WriteLine("[Claviger] : Garbled line: " + cfg + ", the program will now terminate.");
            System.Environment.Exit(1);
            break;
          }
        }
      } catch (FileNotFoundException) {
        Console.WriteLine("[Claviger] : Could not read configuration file.");
        System.Environment.Exit(1);
      }
 
      new List < int > (new int[Console.WindowWidth]).ForEach(i => Console.Write("-"));
      MainConnectionThread.Start();
    }
 
    private static void HandleDisconnected(object sender, DisconnectedEventArgs e) {
      Console.Write("\n[Claviger] : Disconnected...");
      if (!StabilizeChecks.ContainsKey('l')) {
        StabilizeChecks.Add('l', false);
        return;
      }
      StabilizeChecks['l'] = false;
    }
 
    private static void HandleEventQueueRunning(object sender, EventQueueRunningEventArgs e) {
      Console.Write("\n[Claviger] : Event queue started...");
      if (!StabilizeChecks.ContainsKey('e')) {
        StabilizeChecks.Add('e', true);
        return;
      }
      StabilizeChecks['e'] = true;
    }
 
    private static void HandleSimulatorConnected(object sender, SimConnectedEventArgs e) {
      Console.Write("\n[Claviger] : Simulator connected...");
      if (!StabilizeChecks.ContainsKey('s')) {
        StabilizeChecks.Add('s', true);
        return;
      }
      StabilizeChecks['s'] = true;
    }
 
    private static void HandleSimulatorDisconnected(object sender, SimDisconnectedEventArgs e) {
      if (Client.Network.Simulators.Count >= 1) return;
      Console.Write("\n[Claviger] : Simulators disconnected...");
      if (!StabilizeChecks.ContainsKey('s')) {
        StabilizeChecks.Add('s', false);
        return;
      }
      StabilizeChecks['s'] = false;
    }
 
    private static void HandleAppearanceSet(object sender, AppearanceSetEventArgs e) {
      if (e.Success) {
        Console.Write("\n[Claviger] : Appearance set...");
        if (!StabilizeChecks.ContainsKey('a')) {
          StabilizeChecks.Add('a', true);
          return;
        }
        StabilizeChecks['a'] = true;
        return;
      }
      Console.Write("\n[Claviger] : Failed to set appearance...");
      if (!StabilizeChecks.ContainsKey('a')) {
        StabilizeChecks.Add('a', false);
        return;
      }
      StabilizeChecks['a'] = false;
    }
 
    private static void HandleLoginProgress(object sender, LoginProgressEventArgs e) {
      switch (e.Status) {
      case LoginStatus.Success:
        Console.Write("\n[Claviger] : Login ok...");
        StabilizeChecks['l'] = true;
        break;
      case LoginStatus.Failed:
        Console.Write("\n[Claviger] : Failed Login...");
        if (!StabilizeChecks.ContainsKey('l')) {
          StabilizeChecks.Add('l', false);
          break;
        }
        StabilizeChecks['l'] = false;
        break;
      }
    }
 
    private static void HandleFriendshipOffered(object sender, FriendshipOfferedEventArgs e) {
      if (!_masters.Contains(e.AgentID)) return;
      Console.Write("\n[Claviger] : Accepting friendship with " + e.AgentName + ".");
      Client.Friends.AcceptFriendship(e.AgentID, e.SessionID);
    }
 
    private static void MainConnection() {
      StabilizeChecks = new Dictionary < char, bool > ();
      Client.Network.BeginLogin(new LoginParams(Client, _firstName, _lastName, _password, "[WaS] Corrade", "1.0"));
      Console.Write("\n[Corrade] : Stabilizing, please wait...");
      do {
        Console.Write(".");
        Thread.Sleep(1000);
      } while (StabilizeChecks.Count != 4 && !StabilizeChecks.ContainsValue(false));
      if (!StabilizeChecks.ContainsValue(false)) Console.Write("\n[Corrade] : Fully connected and accepting commands.");
      do {
        Thread.Sleep(1000);
        if (StabilizeChecks.ContainsValue(false)) {
          Console.Write("\n[Corrade] : Disconnected, shutting down...");
          _run = false;
        }
      }
      while (_run);
      Console.Write("\n[Corrade] : All operations completed. Quitting.");
      new List < int > (new int[Console.WindowWidth]).ForEach(i => Console.Write("-"));
      Client.Network.Logout();
      Client.Network.Shutdown(NetworkManager.DisconnectType.ClientInitiated);
    }
 
    private static void HandleSelfIM(object sender, InstantMessageEventArgs e) {
      if (e.IM.Dialog.Equals(InstantMessageDialog.MessageBox)) {
        Console.Write("\n[Claviger] : [Server Message] : " + e.IM.Message.Replace(System.Environment.NewLine, " "));
        return;
      }
      if (e.IM.Dialog.Equals(InstantMessageDialog.RequestTeleport)) {
        try {
          foreach(string cfg in File.ReadLines("Claviger.config")) {
            string[] data = cfg.Split(new char[] {
              ':'
            }, 3);
            switch (data[0]) {
            case "master":
              var master = UUID.Zero;
              if (!UUID.TryParse(data[1], out master)) continue;
              if (_masters.Contains(master)) continue;
              _masters.Add(master);
              break;
            }
          }
        } catch (FileNotFoundException) {
          Console.Write("\n[Claviger] : Could not read configuration file.");
          System.Environment.Exit(1);
        }
        if (!_masters.Contains(e.IM.FromAgentID)) return;
        Console.Write("\n[Claviger] : Accepting teleport lure.");
        Client.Self.TeleportLureRespond(e.IM.FromAgentID, e.IM.IMSessionID, true);
        return;
      }
      // Ignore group chat.
      if (e.IM.GroupIM) return;
      var msg = e.IM.Message.Split(new char[] {
        ' '
      });
      if (!msg[0].Equals("auth")) goto NoAuth;
      try {
        foreach(string cfg in File.ReadLines("Claviger.config")) {
          string[] data = cfg.Split(new char[] {
            ':'
          }, 3);
          switch (data[0]) {
          case "secret":
            if (!data[1].Equals(msg[1])) break;
            if (!AuthAgents.Contains(e.IM.FromAgentName)) AuthAgents.Add(e.IM.FromAgentName);
            Console.Write("\n[Claviger] : Authenticated: " + e.IM.FromAgentName + ".");
            break;
          }
        }
      } catch (FileNotFoundException) {
        Console.Write("\n[Claviger] : Could not read configuration file.");
        System.Environment.Exit(1);
      }
      return;
      NoAuth:
      if(!AuthAgents.Contains(e.IM.FromAgentName)) return;
      if (msg[0].Equals("s")) goto SitCommand;
      return;
 
      SitCommand: if (!UUID.TryParse(msg[1], out _sitTargetUUID)) return;
      Primitive targetPrim = Client.Network.CurrentSim.ObjectsPrimitives.Find(
      delegate(Primitive prim) {
        return prim.ID == _sitTargetUUID;
      });
      if (targetPrim == null) return;
      Console.Write("\n[Claviger] : Sitting on: " + targetPrim.ID.ToString() + ".");
      Client.Self.RequestSit(targetPrim.ID, Vector3.Zero);
      Client.Self.Sit();
    }
 
    private static void HandleScriptQuestion(object sender, ScriptQuestionEventArgs e) {
      if (e.ItemID != _sitTargetUUID) return;
      Client.Self.ScriptQuestionReply(Client.Network.CurrentSim, e.ItemID, e.TaskID, ScriptPermission.TriggerAnimation);
    }
  }
}