Wir programmieren einen Bot

Um nun endlich mal vor ran zu kommen, starte Ich hier auf meinem Blog eine Neue Reihe.

Auch wenn ich kein begabter Schreiber bin kann ich trotz allem sehr begabt auf der Tastatur rumhacken.

Dies soll kein Tutorial darstellen und auch keine Anleitung zum Programmieren lernen.
Ich gehe pauschal davon aus das meine Leser ein fundiertes Grundwissen haben. Also ehr mehr wisst als ich? Ich bin gespannt. Challenge accepted! Muhahahah 😉

Okay fangen wir ganz einfach an. Ich arbeite hier mit Visual Studio 2013 da ich beschlossen habe das ganze in C# zu realisieren. Der Bot selbst soll uns im Spiel Ingress unterstützen. Also erstellen wir ein neues Projekt. Welche Art von Projekt man nimmt ist im Grunde wurscht.. ein Texteditor tut es auch! Da mir das aber zu stressig ist und ich nicht auf den Komfort einer IDE verzichten möchte wähle ich ein .. ähm .. WebProjekt. [ASP.net mit MVC]

WebProjekt? WTF?

Ein Bot im WebProjekt? Klar warum nicht?

Meine Wahl beruht auf zwei Überlegungen. Wie oft sitze ich am eigenen Rechner und wo kann ich Software installieren. Nicht oft und ehr weniger im Büro.

Also baue ich das ganze im Web und hoste es auch noch kostenlos bei Azure, also perfekt!

Zack 5 Mausklicks später, das Projekt steht. Na ja, zumindest der Grundrahmen… Einmal auf Azure veröffentlichen und schon ist es auf meiner Azure Webseite verfügbar.

Wie man sieht ist direkt eine „fertig“ Webseite online mit allem was man so braucht. Jetzt das tollste:

Wir implementieren die Google Auth Klamotten damit wir auch direkt mit unserem Spiele Account eingeloggt sind und den Token haben 🙂

Fangen wir mal an:

Öffne App_Start\Startup.Auth.cs

jetzt wird es kniffelig…

aus der letzten Zeile:

// app.UseGoogleAuthentication();

die Kommentar Slash [ // ] entfernen. Fertig.
Gut das war nun wirklich extrem komplex, dafür haben wir nun schon mal einen wichtigen Schritt erledigt. Der Token für die Ingress API steht uns nach dem Login zur Verfügung, cool. Dann testen wir mal den Google Login.  Klappt.

Nun benötigen wir eine Kommunikationsschnittstelle zu Ingress.
Also eine neue Klasse erstellen im neuen Ordner „Bot“ nennen wir sie mal… „IngressWebservice“ und lassen sie im Namespace Bot.

Was brauchen wir noch …
Ein Model für die Points ( Bot.Points)

Soll erstmal reichen da wir erstmal nur versuchen die Points von unserem aktuellem Standort abzurufen. Btw Standort, erstellen wir doch noch ne Klasse GPSControler.
Wir haben zwar im Webserver kein GPS Empfänger, wir wollen aber auch keine GPSDaten auslesen. Wir wollen unsere eigenen GPS Daten nutzen.

Wieder zurück im IngressWebservice:

erstmal ne public Points gePoints() { } Funktion um an die Punkt Daten zu kommen.
Mit dieser Funktion bauen wir erstmal die Kommunikation mit der Ingress API auf und  plaudern mit Ihr. Wie machen wir dass?

Als erstes müssen wir wissen wie die Ingress APP denn mit dem Server kommuniziert.
Das macht sie natürlich über ein Webservice und den findet man unter https://www.ingress.com/r/ also man findet ihn nicht wirklich hier, es kommt eine Weiterleitung und es gibt auch keine öffentliche Dokumentation zur API.. leider …
Aber wenn wir uns den APP Quellcode von Ingress anschauen können wir uns das alles ansehen. Anleitung zum Beispiel: hier

Am besten auch gleich Android Studio mit allem benötigten Zeug installieren.

Auslesen der punkte: /r/or74v5e0dijoseab
Irgendwas für den Chat: /r/tx9a36zfmalr5s35
Game Entinits: r/ivrsh7tuvj0if77k
Action Informationen r/tx9a36zfmalr5s35

Da es eine Rest API ist laufen alle informationen also über JSON \o/ das ist Spitze.

Das abrufen der Punkte würde dann in etwa so aussehen, dieser ganze unleserliche kram sind die geforderten Informationen. Bei meinem letzten Bot war das noch alles schön lesbar. Jetzt ist es verschlüsselt und sicher und keiner kann mehr die Api unberechtigt nutzen. So die Theorie.

{
„tnpkzqqu5p3mn4rp“:“or74v5e0dijoseab“,
„y76begpg0lz0g5s3″:“3aad79b9e7b135fa9c8b2c67bf6a051b0e0617f7“,
„l2pa0wslrtmthlhf“:64,
„s3xowgcoqduol0ij“:-90000000,
„cvv98kwi14wsfesr“:-180000000,
„as1getmlsyn7cyrg“:90000000,
„9xrrc4me7owrbazc“:180000000,
„kz90pcxjj0nypj0a“:0
}

für Portale dann ehr dass hier:

// Portal
{„cprnnhnm9s44iu39“:[„1_34055_21824″,“1_34056_21824″,“1_34055_21826″,“1_34056_21826″],“tnpkzqqu5p3mn4rp“:“ivrsh7tuvj0if77k“,“y76begpg0lz0g5s3″:“3aad79b9e7b135fa9c8b2c67bf6a051b0e0617f7″,“l2pa0wslrtmthlhf“:52,“s3xowgcoqduol0ij“:-90000000,“cvv98kwi14wsfesr“:-180000000,“as1getmlsyn7cyrg“:90000000,“9xrrc4me7owrbazc“:180000000}

Ingress sicher zu gestalten ist echt nicht einfach da der Code, in Java geschrieben wurde, einfach wieder „lesbar“ gemacht werden kann. Im Grunde OpenSource, am einfachsten ist es das ganze auch so zu handhaben und die „Community“ den Rest machen zu lassen 😉

Wie man sehen kann übergibt man die GEO Daten auch an die API, also brauchen wir uns diese auch nur zusammen schupsen und mitliefern.

Als nächstes müssen wir nur noch raus finden was von den Werten was sein soll…
Okay:

„s3xowgcoqduol0ij“:-90000000,
„cvv98kwi14wsfesr“:-180000000,
„as1getmlsyn7cyrg“:90000000,
„9xrrc4me7owrbazc“:180000000,

Das ist noch einfach. Hier haben wir GEO Daten im Grunde brauchen wir davon nichts aber um „sicher“ zu gehen liefern wir immer schön unsere Geo Position mit. Wir wollen ja nicht auffallen.

So dieser Beitrag ist erst mal lang genug.
Teil 2 wird folgen!

Mantis SOAP API Teil 1 Issue Get and Add

Zur Zeit entwickel ich einen WebService der zwischen Mantis und Zendesk Tickets und Kommentare synchronisiert.
Klingt etwas spektakulär, ist im Grunde aber eine Simple Sache wenn nicht immer alles nicht funktionieren würde.

Die Mantis SOAP Api:

Wenn es nach mir ginge würde da eine REST Api hinterstecken aber gut es geht aber nicht nach mir.

Mir ist aufgefallen das es keine wirklich schöne Dokumentation der Mantis Api geht.
Die Nerds unter Euch denken wahrscheinlich: „docu? … wtf … steht alles in der wsdl“ aber das lesen der wsdl ist auch nicht jedermanns Sache.

Da ich ein fauler Programmierer bin, hab ich mir natürlich nicht die Arbeit gemacht, die Komponenten, Verknüpfungen und den Restlichen Firlefanz selbst zu schreiben.
Hierfür hab ich meiner  Request Komponente einfach eine Methode gegeben die aus dem ganzen WSDL Kram die Klassen und Modele erstellt und sich selbst der Komponente erweitert.

Wunder Bärchen.

Grob sähe dass dann so aus.


function parseXml(xml)
{
httpService = new http();
httpService.setMethod('GET');
httpService.setTimeOut(10);
httpService.setUrl( 'http://www.mantisbt.org/bugs/api/soap/mantisconnect.php?wsdl');
result = httpService.send().getPrefix();
xmlfile = xmlparse(result.filecontent); //Parses the XML

//dump(var="#xmlfile.XmlRoot#",label="dump",format="html",abort=true);
strComponent = 'component {';
if(structKeyExists(xmlfile.XmlRoot, "XmlChildren"))
{
for(node in xmlfile.XmlRoot.XmlChildren)
{
switch(node.xmlName){
//dump(var="#node#",label="dump",format="html",abort=false);
case 'message':
if(structKeyExists(node, "XmlAttributes"))
{
strComponent &= chr(13) & chr(10) &'public function ' & node.XmlAttributes.name;
strComponent &= '(';
paramArray = [];
for(strParam in node.XmlChildren)
{
if(structKeyExists(strParam, "XmlAttributes") && strParam.XmlAttributes.name !='return')
ArrayAppend(paramArray, strParam.XmlAttributes.name);
}
if(arrayLen(paramArray))
strComponent &= arraytolist(paramArray);
strComponent &= ')'& chr(13) & chr(10) &'{'&chr(10)&chr(13);
for(strParam in node.XmlChildren)
{
if(structKeyExists(strParam, "XmlAttributes") && strParam.XmlAttributes.name !='return')
strComponent &= chr(9)&'param name="' & strParam.XmlAttributes.name & '" type="' & Mid(strParam.XmlAttributes.type,5,len(strParam.XmlAttributes.type)-3) & '";'&chr(10)&chr(13);
}
}
strComponent &= '}'&chr(10)&chr(13);

break;
case 'types':
for(comps in node.XmlChildren[1].XmlChildren)
{
if(structKeyExists(comps, "XmlAttributes"))
{
if(comps.xmlName == 'xsd:complexType')
{
newComp ='component ' & comps.XmlAttributes.name &chr(10)&chr(13)& ' {'&chr(10)&chr(13);
for(strParam in comps.XmlChildren)
{
for(strP in strParam.XmlChildren)
{
if(structKeyExists(strP, "XmlAttributes"))
if(structKeyExists(strP.XmlAttributes, "name"))
newComp &= chr(9)&'property name="' & strP.XmlAttributes.name & '" type="' & Mid(strP.XmlAttributes.type,5,len(strP.XmlAttributes.type)-3) & '";'&chr(10)&chr(13);
}
}
newComp &= "function toXML()
{
data = getMetaData(this);
xml = '';
for (prop in data.properties)
{
xml &= '<'&prop.name&'>';
switch(prop.type)
{
case 'string':
if(structKeyExists(this, prop.name))
xml &=this[prop.name];
break;
case 'dateTime':
if(structKeyExists(this, prop.name))
xml &=this[prop.name];
break;
case 'boolean':
if(structKeyExists(this, prop.name))
xml &=this[prop.name];
break;
case 'integer':
if(structKeyExists(this, prop.name))
xml &=this[prop.name];
break;
default:
if(refind('Array',prop.name))
{
if(structKeyExists(this, prop.name))
xml &= arrayToList(this[prop.name]);
}
else
{
Comp = createObject(prop.type);
Comp.toXml();
}
break;
}
xml &='';
}
return xml;
}";
newComp &= '}';
FileWrite(comps.XmlAttributes.name&'.cfc',newComp);
}
}
}
break;
}
}
}
strComponent &= '}'&chr(10)&chr(13);
FileWrite('mantis.cfc',strComponent);
//dump(var="#strComponent#",label="dump",format="html",abort=true);
}

MEMO (an mich): Ich brauch hier im Blog mal einen ordentlichen Code Parser -.-

Gut dann hab ich alles was ich benötige zur Hand und kann die Model dem Manger geben.

Die wichtigsten Methoden möchte ich hier einmal aufführen:

Diese Methode holt sich den Eintrag der übergebenden ID

public function issue_get(issue_id)
{
param name="issue_id" type="number";

return '<mc_issue_get>'
& '<username>'
& this.username
& '</username>'
& '<password>'
& this.password
& '</password>'
& '<issue_id>'
& issue_id
& '</issue_id>'
& '</mc_issue_get>';
}

Diese Methode erstellt einen neuen Eintrag.
issue._toString() ist eine von mir überschriebene Methode die mir aus den erstellten Modellen sauberes XML zurückliefert um es einfach an die Api zu schicken.


public function issue_add(issue)
{
param name="issue" type="IssueData";

xmlData = ''
& ''
& this.username
& ''
& ''
& this.password
& ''
& ''
& issue._toString()
& ''
& '';
return xmlData;
}