SRL Archive - Senior Scripts and Snippets

Grand Exchange Stock Monitoring Program


Narcle

2008-09-21 21:52:13

Well as the title sorta describes. I was wondering if anyone would be up to the task of making a GE Stock Monitoring type program. I am not very good in other languages at the moment. I was thinking C or C++ would be the best or even a PHP site, but for functionality a personal program that connects to the RS site would be better.

The idea:
Well if you may or may not know by now, the RS site has graphs, prices, change rates (GP & %) and some have sold quantity (probably all but just hidden). Problem with just using the site is you can only look at one item at a time and have to constantly write down things to check prices to compare various items (sets, potions, herbs etc.). Now its not hard to grab the prices, but to grab them all, sort them, save the data then display them the way you want them is the ideal way. To make this as customizable as possible, almost like a real stock graph.

List of consideration:
  • Saving data to have graphs longer then 30 days (90 days, whole years etc.).
  • Customized daily rates. (1 day, 5 day, 20 days, 30 days etc.)
  • Comparing prices - customizable, (herbs to potions, pieces to sets w/ manual variables)
  • Basic graphs, prices, changes.
  • Quantity sold every day (I bet all are there just hidden)
  • Relation graphs (average time it goes up or goes down, price drop in that time etc.)
  • Large scale relations (major price drop/rise in many items in same time period - more explanation below)
  • Predictability on change and the percent?
  • More to think of I'm sure.

*Large scale relations*
Okay let me explain this a bit. There is many clans or private clans that do "Team Stock Marketing." What this is, as you can guess, is where they all buy a very large quantity of one item since there is a limit on trading etc. So when tons of them buy/sell the prices will go up or down (note: this is on a massive quantity). Now its crucial to try and predict these privateers sales as you can imagine. If your trying to market one item then the privateers kick in and sell/buy in massive quantities of the item your trying to merchant if your lucky it'll go in your favor and you profit along with them, but if not your totally fucked. So as you can guess trying to predict these changes is crucial for mass marketing.

For example, my friend and I have noticed major changes in several diverse items that have major price increases/decrease in the same time frame with very similar graphs. So if we can notice the change over many items, then we might be able to predict the next mass team marketing schedule.


You can guess why I'm asking this in SSRL forums first. Firstly majority of the good programmers are in SSRL, secondly to keep this more private.

Again I'm asking if any good programmer is up to the challenge as I am not skilled in this area as of yet. I would like to keep this in SSRL as long as possible. But if nothing happens I might ask in Members forum.

Smartzkid

2008-09-22 17:23:19

Would be pretty neat, but you'd either have to go through all objects every day & save the data, or parse the pictures..

Definitely a fun challenge though.

Narcle

2008-09-22 20:45:38

Yeah, you'd only have to do it "once" a day, also you could just monitory the items you want (less bandwidth). Of course doing this with a server would make this ez, just have the server grab and save all the data.

Boreas

2008-09-22 23:46:51

Any MySQL experts here to put these results into a database?

program New;
var
Page,ObjNum,ObjName,ObjPrice:string;
Letter,PLP1,PLP2,ObjNum1,PageLimit,ObjNum2,ObjName2,ObjPrice2,ObjPrice1,time,t,t1,PageNumber:integer;
ObjPriceN:extended;
PageLimitSet:boolean;

begin
time:=getsystemtime;
Letter:=1;
repeat
PageNumber:=1;
repeat
if chr(64+Letter) = 'Q' then
begin
Letter:=Letter+1;
break;
end;
Page := GetPage('http://itemdb-rs.runescape.com/results.ws?query=&sup=Initial%20Letter&cat='+chr(Letter+64)+'&sub=All&page='+inttostr(PageNumber)+'&vis=1&order=1&sortby=name&price=&members=');
if not PageLimitSet then
begin
//writeln('Setting PageLimit');
PLP1:=Pos('<td class="navright">',Page);
PLP2:=LastPosEx('ers=">',Page,PLP1);
if PLP2=-1 then
begin
PageLimit:=1;
PageLimitSet:=true;
end;
if PageLimitSet=false then
begin
PLP1:=PosEx('</a>',Page,PLP2);
PageLimit:=strtoint(Copy(Page,PLP2+6,PLP1-PLP2-6));
end;
PageLimitSet:=true;
// writeln(chr(letter+64)+' '+inttostr(pagelimit));
end;
t:=Pos('Change Today',Page);
repeat
ObjNum1:=PosEx('em.ws?obj=',Page,t);
if ObjNum1=-1 then
begin
Page:='';
//writeln('Last Item '+inttostr(PageNumber)+' '+inttostr(pagelimit));
break;
end;
ObjNum2:=PosEx('">',Page,ObjNum1);
ObjNum:=Copy(Page,ObjNum1+10,ObjNum2-ObjNum1-10);
ObjName2:=PosEx('</a',Page,ObjNum2);
ObjName:=Copy(Page,ObjNum2+3,ObjName2-ObjNum2-3);
ObjPrice1:=PosEx('<td>',Page,ObjName2);
ObjPrice2:=PosEx('</td>',Page,ObjPrice1);
ObjPrice:=Copy(Page,ObjPrice1+4,ObjPrice2-ObjPrice1-4);
if pos('k',ObjPrice)>0 then
begin
ObjPriceN:=StrToFloat(TrimLetters(ObjPrice));
ObjPriceN:=ObjPriceN*1000;
end;
if pos('m',ObjPrice)>0 then
begin
ObjPriceN:=StrToFloat(TrimLetters(ObjPrice));
ObjPriceN:=ObjPriceN*1000000;
end;
ObjPrice:=TrimOthers(ObjPrice);
if (pos('k',ObjPrice)=0)and(pos('m',ObjPrice)=0) then
ObjPriceN := strtofloat(ObjPrice);

writeln(ObjNum+' '+ObjName+' '+floattostr(ObjPriceN));
//Dump to file, send to database, etc
t1:=t1+1;
t:=ObjName2;
until isfkeydown(11);
PageNumber:=PageNumber+1;
if PageNumber>PageLimit then
begin
PageLimitSet:=false;
Letter:=Letter+1;
break;
end;
until isfkeydown(11);
if Letter>26 then
break;
until isfkeydown(11);
writeln(inttostr(t1)+' items in '+inttostr(getsystemtime-time)+'ms');
end.

nielsie95

2008-09-23 07:23:31

http://www.grandprices.net/

It seems like the site's a bit broken, but it's the same idea.

Smartzkid

2008-09-23 16:13:51

program New;
const
DBLocation = 'C:\Program Files\SCAR 3.15\ItemDB\';

var
Page, ObjNum, ObjName, ObjPrice: String;

Letter, PLP1, PLP2, ObjNum1, PageLimit,
ObjNum2, ObjName2, ObjPrice2, ObjPrice1,
time, t, t1, PageNumber, theFile :integer;

ObjPriceN :extended;

PageLimitSet :boolean;

begin
time:=getsystemtime;
theFile := RewriteFile(DBLocation + Replace(DateToStr(Date), '/', '_') + '.idb', false);
Letter:=1;
repeat
PageNumber:=1;
repeat
if chr(64+Letter) = 'Q' then
begin
Letter:=Letter+1;
break;
end;
Page := GetPage('http://itemdb-rs.runescape.com/results.ws?query=&sup=Initial%20Letter&cat='+chr(Letter+64)+'&sub=All&page='+inttostr(PageNumber)+'&vis=1&order=1&sortby=name&price=&members=');
if not PageLimitSet then
begin
//writeln('Setting PageLimit');
PLP1:=Pos('<td class="navright">',Page);
PLP2:=LastPosEx('ers=">',Page,PLP1);
if PLP2=-1 then
begin
PageLimit:=1;
PageLimitSet:=true;
end;
if PageLimitSet=false then
begin
PLP1:=PosEx('</a>',Page,PLP2);
PageLimit:=strtoint(Copy(Page,PLP2+6,PLP1-PLP2-6));
end;
PageLimitSet:=true;
// writeln(chr(letter+64)+' '+inttostr(pagelimit));
end;
t:=Pos('Change Today',Page);
repeat
ObjNum1:=PosEx('em.ws?obj=',Page,t);
if ObjNum1=-1 then
begin
Page:='';
//writeln('Last Item '+inttostr(PageNumber)+' '+inttostr(pagelimit));
break;
end;
ObjNum2:=PosEx('">',Page,ObjNum1);
ObjNum:=Copy(Page,ObjNum1+10,ObjNum2-ObjNum1-10);
ObjName2:=PosEx('</a',Page,ObjNum2);
ObjName:=Copy(Page,ObjNum2+3,ObjName2-ObjNum2-3);
ObjPrice1:=PosEx('<td>',Page,ObjName2);
ObjPrice2:=PosEx('</td>',Page,ObjPrice1);
ObjPrice:=Copy(Page,ObjPrice1+4,ObjPrice2-ObjPrice1-4);
if pos('k',ObjPrice)>0 then
begin
ObjPriceN:=StrToFloat(TrimLetters(ObjPrice));
ObjPriceN:=ObjPriceN*1000;
end;
if pos('m',ObjPrice)>0 then
begin
ObjPriceN:=StrToFloat(TrimLetters(ObjPrice));
ObjPriceN:=ObjPriceN*1000000;
end;
ObjPrice:=TrimOthers(ObjPrice);
if (pos('k',ObjPrice)=0)and(pos('m',ObjPrice)=0) then
ObjPriceN := strtofloat(ObjPrice);

//writeln(ObjNum+': '+ObjName+' '+floattostr(ObjPriceN));
WriteFileString(theFile, '<n>' + ObjName + '</n> ' + '<id>' + ObjNum + '</id> ' + '<gp>' + floatToStr(ObjPriceN) + '</gp>' + chr(10));
//Dump to file, send to database, etc
t1:=t1+1;
t:=ObjName2;
until isfkeydown(11);
PageNumber:=PageNumber+1;
if PageNumber>PageLimit then
begin
PageLimitSet:=false;
Letter:=Letter+1;
break;
end;
until isfkeydown(11);
if Letter>26 then
break;
until isfkeydown(11);
CloseFile(theFile);
writeln(inttostr(t1)+' items in '+inttostr(getsystemtime-time)+'ms');
end.

Made Boreas' code output to a relatively easy to parse file. Took me 3 mins, 48 seconds to download & save.

Will work on a grapha after I finish my homework.

EDIT: Grapher is done, will post asap

EDIT: Grapher had some weird stuff going on with the gridlines, fixed it all, but somehow made gridline spacing pixel based rather than unit based. Here's the current grapher, the point plotting is accurate, but I'm going to have to work on the gridline algorithm before everything looks accurate. Also, if you don't force the axis to be visible, gridlines and axislines go waay out of sync with the graph. Working on this problem also.

EDIT: Still trying to fix the pesky gridlines..

TODO:
-Fix gridlines (priority)
-Axis-disable feature
-Axis movement bug

//GraphPxlsX, GraphPxlsY -> Size of graph on screen
//pts: TPointArray -> Data to be graphed
//GraphBCGColor, GraphAxisColor, GraphLineColor -> Various colors
//zoom -> How much to zoom the graph output
//Axis_ForceVisible -> Forces the origin (0,0) to be visible in the graph
//gridlines -> Shows gridlines
//GridlineXSpacing, GridlineYSpacing -> How far apart the gridlines should be
//GridlineColor -> Gridline color

program New;

procedure GraphPtsEx(GraphPxlsX, GraphPxlsY: integer; pts: TPointArray; GraphBCGColor, GraphAxisColor, GraphLineColor, zoom: integer; Axis_ForceVisible: boolean; gridlines: boolean; GridlineXSpacing, GridlineYSpacing, GridlineColor: integer);
var
x_min, x_max, y_min, y_max: integer;
i, temp: integer;
scaleX, scaleY: extended;
Graph_BMP: integer;
begin
//Param-error catching
if(GraphPxlsX = 0)then
Exit;
if(GraphPxlsY = 0)then
Exit;
if(zoom = 0)then
Exit;
if(GridlineXSpacing = 0)then
Exit;
if(GridlineYSpacing = 0)then
Exit;

//Create the bitmap that the graph will be displayed on and fill with a given color
DisplayDebugImgWindow(GraphPxlsX * zoom, GraphPxlsY * zoom);
Graph_BMP := BitmapFromString(GraphPxlsX, GraphPxlsY, '');
FastDrawClear(Graph_BMP, GraphBCGColor);

if(Axis_ForceVisible = false)then
begin
x_min := pts[0].x;
y_min := pts[0].y;
end;

//Find lowest and highest points
for i := 0 to high(pts) do
begin
if(pts[i].x < x_min)then
x_min := pts[i].x;
if(pts[i].x > x_max)then
x_max := pts[i].x;
if(pts[i].y < y_min)then
y_min := pts[i].y;
if(pts[i].y > y_max)then
y_max := pts[i].y;
end;

//add a 10% 'buffer' to the x axis
temp := (round(abs(x_max) + abs(x_min))) / 10;
x_min := x_min - temp;
x_max := x_max + temp;

//add a 10% 'buffer' to the y axis
temp := (round(abs(y_max) + abs(y_min))) / 10;
y_min := y_min - temp;
y_max := y_max + temp;

//Debug
{writeln(inttostr(x_min));
writeln(inttostr(x_max));
writeln(inttostr(y_min));
writeln(inttostr(y_max));}

//Determine Graph-point to pixel scale
scaleX := GraphPxlsX / round(abs(x_max) + abs(x_min));
scaleY := GraphPxlsY / round(abs(y_max) + abs(y_min));

//Debug
writeln(floattostr(scaleX));
writeln(floattostr(scaleY));

//GridlineXSpacing := round(GridlineXSpacing * ScaleX);
//GridlineYSpacing := round(GridlineYSpacing * ScaleY);

//Draw vertical gridlines
for temp := 0 to round(GraphPxlsX / (GridlineXSpacing * ScaleX)) do
//Reason for following if statement: Sometimes the gridline plotting algorithm would plot a gridline at a location outside the picture; this would sometimes throw an access violation error, but other times, it would simply wrap the pixels over to the other side of the bitmap, causing erratic gridlines to pop up in very strangle places
if((GridlineXSpacing * temp) + (round(abs(x_min) * scaleX) mod GridlineXSpacing) <= 125)then
for i := 0 to GraphPxlsX - 1 do
FastSetPixel(Graph_BMP, round(GridlineXSpacing * temp * ScaleX) + (round(abs(x_min) * scaleX) mod GridlineXSpacing), i, GridlineColor);

//round((abs(x_min) * scaleX) + pts[i].x * scaleX)
//round(GraphPxlsY - (abs(y_min * scaleY) + (pts[i].y * scaleY)))

//Draw horizontal gridlines
for temp := 0 to round(GraphPxlsY / (GridlineYSpacing * ScaleY)) do
//Read similar note above
if((GridlineYSpacing * temp) + (round(((GraphPxlsY / ScaleY) - abs(y_min)) * scaleY) mod GridlineYSpacing) <= 125)then
for i := 0 to GraphPxlsY - 1 do
FastSetPixel(Graph_BMP, i, round(GridlineYSpacing * temp * ScaleY) + (round(((GraphPxlsY / ScaleY) - abs(y_min)) * scaleY) mod GridlineYSpacing), GridlineColor);
// writeln(inttostr((GridlineYSpacing * temp) + (round(((GraphPxlsY / ScaleY) - abs(y_min)) * scaleY) mod GridlineYSpacing)));


//Draw vertical axis
for i := 0 to GraphPxlsX - 1 do
FastSetPixel(Graph_BMP, round(abs(x_min) * scaleX), i, GraphAxisColor);

//Draw horizontal axis
for i := 0 to GraphPxlsY - 1 do
FastSetPixel(Graph_BMP, i, round(((GraphPxlsY / ScaleY) - abs(y_min)) * scaleY), GraphAxisColor);


//Graph the points
for i := 0 to high(pts) do
begin
//Debug
//writeln(inttostr(round(((0 - x_min) * scaleX) + pts[i].x * scaleX)));
//writeln(inttostr(round(GraphPxlsY - ((0 - (y_min * scaleY)) + (pts[i].y * scaleY)))));
try
FastSetPixel(Graph_BMP, round((abs(x_min) * scaleX) + pts[i].x * scaleX), round(GraphPxlsY - (abs(y_min * scaleY) + (pts[i].y * scaleY))) {round((GraphPxlsY - (0 - (y_min * scaleY))) - (y_min * scaleY + pts[i].y * scaleY))}, GraphLineColor);
except
writeln('Error at pt ' + inttostr(i + 1));
end;
end;

//Display graph and free resources
SafeCopyCanvas(GetBitmapCanvas(Graph_BMP), GetDebugCanvas, 0, 0, GraphPxlsX, GraphPxlsY, 0, 0, GraphPxlsX * zoom, GraphPxlsY * zoom);
FreeBitmap(Graph_BMP);
end;

//Displays graph with white background, brown axis, and black points
procedure GraphPts(GraphPxlsX, GraphPxlsY: integer; pts: TPointArray);
begin
GraphPtsEx(GraphPxlsX, GraphPxlsY, pts, 16777215, 5018076, 0, 1, false, true, 10, 10, 16701619);
end;


begin
// GraphPtsEx(125, 125, [Point(5, 3){, Point(-5, -7), Point(0, 0), Point(1, 1)}, Point(5, 5), Point(10, 10)], 16777215, 5018076, 0, 3, false, true, 10, 10, 16701619);
GraphPtsEx(125, 125, [Point(0, 0), Point(1, 1), Point(5, 5), Point(10, 10)], 16777215, 5018076, 0, 3, true, true, 1, 1, 16701619);
end.

Narcle

2008-09-23 17:17:44

From: nielsie95
http://www.grandprices.net/

It seems like the site's a bit broken, but it's the same idea.


Yeah I know niel. But its not as detailed as what I'm trying to push for here.

@Boreas & Smartzkid
Awesome guys, I'll mess around with those later. I want to see all the info RS saves.

Boreas

2008-09-23 22:25:11

You mentioned that some items show the sold quantity. Do you have a link to one such item?

Narcle

2008-09-23 23:24:30

From: Boreas
You mentioned that some items show the sold quantity. Do you have a link to one such item?


There's a top 100 items sold list. Umm

http://itemdb-rs.runescape.com/top100.ws?list=0&scale=0

Also still haven't tried script yet… been excited with new reflection stuff. But I will get to this eventually lol

Boreas

2008-09-24 00:33:28

I don't see a way to find how many have been traded.

Anyways Here is a list (save to scar folder) generated with
program New;
var
Page,ObjNum,ObjName,ObjPrice:string;
iter,Letter,PLP1,PLP2,ObjNum1,PageLimit,ObjNum2,ObjName2,ObjPrice2,ObjPrice1,time,t,t1,PageNumber:integer;
ObjPriceN:extended;
PageLimitSet:boolean;

begin
time:=getsystemtime;
Letter:=1;
repeat
PageNumber:=1;
repeat
if chr(64+Letter) = 'Q' then
begin
Letter:=Letter+1;
break;
end;
Page := GetPage('http://itemdb-rs.runescape.com/results.ws?query=&sup=Initial%20Letter&cat='+chr(Letter+64)+'&sub=All&page='+inttostr(PageNumber)+'&vis=1&order=1&sortby=name&price=&members=');
if not PageLimitSet then
begin
//writeln('Setting PageLimit');
PLP1:=Pos('<td class="navright">',Page);
PLP2:=LastPosEx('ers=">',Page,PLP1);
if PLP2=-1 then
begin
PageLimit:=1;
PageLimitSet:=true;
end;
if PageLimitSet=false then
begin
PLP1:=PosEx('</a>',Page,PLP2);
PageLimit:=strtoint(Copy(Page,PLP2+6,PLP1-PLP2-6));
end;
PageLimitSet:=true;
// writeln(chr(letter+64)+' '+inttostr(pagelimit));
end;
t:=Pos('Change Today',Page);
repeat
ObjNum1:=PosEx('em.ws?obj=',Page,t);
if ObjNum1=-1 then
begin
Page:='';
//writeln('Last Item '+inttostr(PageNumber)+' '+inttostr(pagelimit));
break;
end;
ObjNum2:=PosEx('">',Page,ObjNum1);
ObjNum:=Copy(Page,ObjNum1+10,ObjNum2-ObjNum1-10);
ObjName2:=PosEx('</a',Page,ObjNum2);
ObjName:=Copy(Page,ObjNum2+3,ObjName2-ObjNum2-3);
ObjPrice1:=PosEx('<td>',Page,ObjName2);
ObjPrice2:=PosEx('</td>',Page,ObjPrice1);
ObjPrice:=Copy(Page,ObjPrice1+4,ObjPrice2-ObjPrice1-4);
if pos('k',ObjPrice)>0 then
begin
ObjPriceN:=StrToFloat(TrimLetters(ObjPrice));
ObjPriceN:=ObjPriceN*1000;
end;
if pos('m',ObjPrice)>0 then
begin
ObjPriceN:=StrToFloat(TrimLetters(ObjPrice));
ObjPriceN:=ObjPriceN*1000000;
end;
ObjPrice:=TrimOthers(ObjPrice);
if (pos('k',ObjPrice)=0)and(pos('m',ObjPrice)=0) then
ObjPriceN := strtofloat(ObjPrice);

//writeln(ObjNum+' '+ObjName+' '+floattostr(ObjPriceN));

WriteINI('NumberToName',ObjNum,ObjName,AppPath+'GE.ini');
WriteINI('NameToNumber',ObjName,ObjNum,AppPath+'GE.ini');
WriteINI('Search',inttostr(t1)+'U',ObjNum,AppPath+'GE.ini');
WriteINI('Search',inttostr(t1)+'A',ObjName,AppPath+'GE.ini');

//Dump to file, send to database, etc
t1:=t1+1;
t:=ObjName2;
until isfkeydown(11);
PageNumber:=PageNumber+1;
if PageNumber>PageLimit then
begin
PageLimitSet:=false;
Letter:=Letter+1;
break;
end;
until isfkeydown(11);
if Letter>26 then
break;
until isfkeydown(11);
writeln(inttostr(t1)+' items in '+inttostr(getsystemtime-time)+'ms');

end.

usable with
program New;

function NameToNumber(Name:string):integer;
begin
result:=strtoint(ReadINI('NameToNumber',Name,AppPath+'GE.ini'));
end;

function NumberToName(Num:integer):string;
begin
result:=ReadINI('NumberToName',inttostr(Num),AppPath+'GE.ini');
end;

function Search(term:string):integer;
var tmpstr:string;
count,count2:integer;
MyArr:array of integer;
begin
writeln('Press F11 when there are enough results');
writeln('Press F10 to start');
repeat
wait(20);
until isfkeydown(10);
repeat
tmpstr:=ReadIni('Search', inttostr(count)+'A',AppPath+'GE.ini');
if pos(lowercase(term),lowercase(tmpstr))>0 then
begin
writeln(inttostr(count2)+' '+tmpstr);
setarraylength(MyArr,count2+1);
MyArr[count2]:=strtoint(ReadIni('Search', inttostr(count)+'U',AppPath+'GE.ini'));
count2:=count2+1;
end;
count:=count+1;
until ((isfkeydown(11)) or (count=3203));
writeln('Press F12 when you know which number');
repeat
wait(20);
until isfkeydown(12);
result:=MyArr[strtoint(ReadLn('Which number?'))];
end;

function CurrentPrice(ObjNum:integer):integer;
var tmpstr,Page:string;
Pos1,Pos2:integer;
tmpfloat:extended;
begin
Page := GetPage('http://itemdb-rs.runescape.com/viewitem.ws?obj='+inttostr(ObjNum));
Pos1:=Pos('>Market price:</b> ',Page);
Pos2:=PosEx('</span>',Page,Pos1);
tmpstr:=Copy(Page,Pos1+19,Pos2-Pos1-20);
if pos('k',tmpstr)>0 then
begin
tmpfloat:=StrToFloat(TrimLetters(tmpstr));
result:=round(tmpfloat*1000);
end;
if pos('m',tmpstr)>0 then
begin
tmpfloat:=StrToFloat(TrimLetters(tmpstr));
result:=round(tmpfloat*1000000);
end;
tmpstr:=TrimOthers(tmpstr);
if (pos('k',tmpstr)=0)and(pos('m',tmpstr)=0) then
result := strtoint(tmpstr);
Page:='';
end;

begin

writeln(NumberToName(1059));
writeln(inttostr(NameToNumber('Leather gloves')));
//writeln(NumberToName(Search('gold')));
writeln(inttostr(CurrentPrice(8445)));
writeln(inttostr(CurrentPrice(Search(Readln('search for an item')))));

end.

Smartzkid

2008-09-25 16:44:48

program Searcha;
const
Item = 'Mort myre fungus';
var
i, cFile, ID: integer;
SearchItem, path, unparsedList: String;
files: TStringArray;
prices: TIntegerArray;
begin
SearchItem := lowercase(Item);
SearchItem[1] := uppercase(SearchItem[1])[1];
path := ExtractFilePath(GetApplication.ExeName) + '\ItemDB\';
files := GetFiles(path, 'idb');
SetLength(prices, length(files));
for i := 0 to high(files) do
begin
cFile := OpenFile(path + files[i], true);
ReadFileString(cFile, unparsedList, FileSize(cFile));
try
ID := StrToInt(Between('<n>' + SearchItem + '</n> <id>', '</id>', unparsedList));
prices[i] := StrToInt(Between('<id>' + IntToStr(ID) + '</id> <gp>', '</gp>', unparsedList));
except
writeln('Item not found');
end;
CloseFile(cFile);
end;


for i := 0 to high(prices) do
writeln(SearchItem + ' cost ' + inttostr(prices[i]) + ' gp');
end.

Searches the item price databases made by the program in my post above.

If I feel motivated later on, I'll incorporate this, Boreas' currentprice function, and my grapha all into one nice form.

Boreas

2008-09-26 01:30:49

How about something like this?

An item name look up function that can go on partial names, give user choices, and outputs item number (like in my Search except with a nice interface), and a price look up function that takes that item number (like my CurrentPrice) and looks into the database (like your Searcha) and shows price history (like in your graph).

Smartzkid

2008-09-26 16:56:25

program JEW;

var
EndInitForm : Boolean;
frmDesign : TForm;
Settings_BTN : TButton;
SearchTerm : TEdit;
ResultBox : TListBox;
SettingsBox : TGroupBox;
SETT_Label : TLabel;
SET_DynSrch : TCheckBox;
SETT_ClosePanel : TButton;
SETT_DBLoc : TEdit;
ITEM_DtlsPanel : TGroupBox;
Image1 : TImage;
ITEM_Label1 : TLabel;
MarketPrice : TLabel;
MinPrice : TLabel;
MaxPrice : TLabel;
OneWeek : TButton;
TwoWeeks : TButton;
OneMonth : TButton;
ThreeMonths : TButton;
CustomDays : TButton;

procedure InitFormOnClose(Sender : TObject; var Action : TCloseAction);
begin
if (not(frmDesign.ModalResult = 1)) then
EndInitForm := True;
end;

procedure OpenSettings(Sender: TObject);
begin
Settings_BTN.Enabled := false;
SettingsBox.Visible := true;
end;

procedure CloseSettings(Sender: TObject);
begin
Settings_BTN.Enabled := true;
SettingsBox.Visible := false;
end;

procedure Search(Sender: TObject);
begin

end;

procedure SearchChanged(Sender: TObject);
begin
if(SET_DynSrch.Checked)then
Search(Sender);
end;

procedure ChangeInterval(Sender: TObject);
var
Interval: integer;
begin
OneWeek.Enabled := true;
TwoWeeks.Enabled := true;
OneMonth.Enabled := true;
ThreeMonths.Enabled := true;
CustomDays.Enabled := true;
CustomDays.Caption := 'Custom';

//Sender.Enabled := false? SCAR FAIL

case Sender of
OneWeek: OneWeek.Enabled := false;
TwoWeeks: TwoWeeks.Enabled := false;
OneMonth: OneMonth.Enabled := false;
ThreeMonths: ThreeMonths.Enabled := false;
CustomDays: CustomDays.Enabled := false;
end;

case Sender of
OneWeek: Interval := 7;
TwoWeeks: Interval := 14;
OneMonth: Interval := 31;
ThreeMonths: Interval := 31 * 3;
CustomDays:
begin
try
Interval := StrToInt(Readln('Interval (days)?'));
if(Interval < 0)then
Interval := 0 - Interval;
if(Interval = 0)then
Interval := 20;
CustomDays.Caption := IntToStr(Interval) + ' Days';
except
// CreateMessageDialog('Please enter a number', mrOk, mrOk);
Interval := 20;
CustomDays.Caption := IntToStr(Interval) + ' Days';
end;
end;
end;
end;



// This form was parsed using DFM Form Parser v.26c Beta by Ron.
// Generated from GE_DB_Form.dfm.
procedure InitForm;
begin
frmDesign := CreateForm;
with frmDesign do
begin
OnClose := @InitFormOnClose;
Position := poScreenCenter;
BorderStyle := bsSingle;
BorderIcons := [biSystemMenu];
Width := 600;
Height := 373;
Caption := 'Grand Exchange Wizard (JEW)';
Color := clBtnFace;
Font.Color := clWindowText;
Font.Height := -11;
Font.Name := 'MS Sans Serif';
Font.Style := [];
PixelsPerInch := 96;
end;
Settings_BTN := TButton.Create(frmDesign);
with Settings_BTN do
begin
Parent := frmDesign;
Left := 192;
Top := 8;
Width := 65;
Height := 17;
Caption := 'Settings';
TabOrder := 0;
OnClick := @OpenSettings;
end;
SearchTerm := TEdit.Create(frmDesign);
with SearchTerm do
begin
Parent := frmDesign;
Left := 8;
Top := 8;
Width := 169;
Height := 21;
TabOrder := 1;
Text := 'Search';
SearchTerm.OnChange := @Search;
SearchTerm.OnEnter := @Search;
end;
ResultBox := TListBox.Create(frmDesign);
with ResultBox do
begin
Parent := frmDesign;
Left := 8;
Top := 40;
Width := 249;
Height := 289;
ItemHeight := 13;
TabOrder := 2;
end;
SettingsBox := TGroupBox.Create(frmDesign);
with SettingsBox do
begin
Parent := frmDesign;
Left := 8;
Top := 8;
Width := 249;
Height := 321;
Caption := 'Settings';
TabOrder := 3;
Visible := False;
end;
SETT_Label := TLabel.Create(SettingsBox);
with SETT_Label do
begin
Parent := SettingsBox;
Left := 8;
Top := 48;
Width := 89;
Height := 13;
Caption := 'Database location:';
end;
SET_DynSrch := TCheckBox.Create(SettingsBox);
with SET_DynSrch do
begin
Parent := SettingsBox;
Left := 8;
Top := 16;
Width := 153;
Height := 17;
Caption := 'Search while typing query';
TabOrder := 0;
end;
SETT_ClosePanel := TButton.Create(SettingsBox);
with SETT_ClosePanel do
begin
Parent := SettingsBox;
Left := 176;
Top := 296;
Width := 65;
Height := 17;
Caption := 'Save';
TabOrder := 1;
Onclick := @CloseSettings;
end;
SETT_DBLoc := TEdit.Create(SettingsBox);
with SETT_DBLoc do
begin
Parent := SettingsBox;
Left := 8;
Top := 64;
Width := 233;
Height := 21;
TabOrder := 2;
Text := 'SETT_DBLoc';
end;
ITEM_DtlsPanel := TGroupBox.Create(frmDesign);
with ITEM_DtlsPanel do
begin
Parent := frmDesign;
Left := 264;
Top := 8;
Width := 321;
Height := 321;
Caption := 'Item Details: (itemname) [ID]';
TabOrder := 4;
end;
Image1 := TImage.Create(ITEM_DtlsPanel);
with Image1 do
begin
Parent := ITEM_DtlsPanel;
Left := 8;
Top := 24;
Width := 305;
Height := 193;
end;
ITEM_Label1 := TLabel.Create(ITEM_DtlsPanel);
with ITEM_Label1 do
begin
Parent := ITEM_DtlsPanel;
Left := 8;
Top := 248;
Width := 33;
Height := 13;
Caption := 'Today:';
end;
MarketPrice := TLabel.Create(ITEM_DtlsPanel);
with MarketPrice do
begin
Parent := ITEM_DtlsPanel;
Left := 19;
Top := 264;
Width := 36;
Height := 13;
Caption := 'Market:';
end;
MinPrice := TLabel.Create(ITEM_DtlsPanel);
with MinPrice do
begin
Parent := ITEM_DtlsPanel;
Left := 35;
Top := 280;
Width := 20;
Height := 13;
Caption := 'Min:';
end;
MaxPrice := TLabel.Create(ITEM_DtlsPanel);
with MaxPrice do
begin
Parent := ITEM_DtlsPanel;
Left := 32;
Top := 296;
Width := 23;
Height := 13;
Caption := 'Max:';
end;
OneWeek := TButton.Create(ITEM_DtlsPanel);
with OneWeek do
begin
Parent := ITEM_DtlsPanel;
Left := 8;
Top := 224;
Width := 49;
Height := 17;
Caption := '1 Week';
TabOrder := 0;
OnClick := @ChangeInterval;
end;
TwoWeeks := TButton.Create(ITEM_DtlsPanel);
with TwoWeeks do
begin
Parent := ITEM_DtlsPanel;
Left := 64;
Top := 224;
Width := 49;
Height := 17;
Caption := '2 Weeks';
TabOrder := 1;
OnClick := @ChangeInterval;
end;
OneMonth := TButton.Create(ITEM_DtlsPanel);
with OneMonth do
begin
Parent := ITEM_DtlsPanel;
Left := 120;
Top := 224;
Width := 49;
Height := 17;
Caption := '1 Month';
TabOrder := 2;
OnClick := @ChangeInterval;
end;
ThreeMonths := TButton.Create(ITEM_DtlsPanel);
with ThreeMonths do
begin
Parent := ITEM_DtlsPanel;
Left := 176;
Top := 224;
Width := 57;
Height := 17;
Caption := '3 Months';
TabOrder := 3;
OnClick := @ChangeInterval;
end;
CustomDays := TButton.Create(ITEM_DtlsPanel);
with CustomDays do
begin
Parent := ITEM_DtlsPanel;
Left := 240;
Top := 224;
Width := 73;
Height := 17;
Caption := 'Custom';
TabOrder := 4;
OnClick := @ChangeInterval;
end;
end;

procedure SafeInitForm;
var
v : TVariantArray;
begin
SetArrayLength(v, 0);
ThreadSafeCall('InitForm', v);
end;

procedure ShowInitFormModal;
begin
frmDesign.ShowModal;
end;

procedure SafeShowInitFormModal;
var
v : TVariantArray;
begin
SetArrayLength(v, 0);
ThreadSafeCall('ShowInitFormModal', v);
end;

procedure MainInitForm;
begin
try
SafeInitForm;
SafeShowInitFormModal;
finally
FreeForm(frmDesign);
except
WriteLn('An error seems to have occurred in: InitForm');
end;
end;

begin
ClearDebug;
GetSelf.WindowState := wsMinimized;
MainInitForm;
GetSelf.WindowState := wsNormal;
if (EndInitForm) then
TerminateScript;
end.

Not much actual code; all concept. Searcher will take names, partial names, or ID's (good idea, Boreas). The entire Item Details panel won't show until an item in the listbox is double clicked; the panel will also have a close button to collapse it again.

Settings will be stored in an INI inside the database directory

EDIT: Also, I'm going to convert the databases to INI format.

Narcle

2008-09-26 20:52:30

Yeah need a good INI file that can be saved for long periods of time. That way there's a point to all of this (other then ezier management).

Smartzkid

2008-10-02 21:49:36

Well…I would be posting a nearly fully working GE searcher, but SCAR just committed suicide…I think I forgot to break out of a loop =/

Anyone mind asking Freddy to PLEASE add script autosaves?

Bobarkinator

2008-10-02 22:35:18

Someone mind asking Smartzkid to hit ctrl + s when he pauses? :p

Smartzkid

2008-10-04 23:10:21

Added:
-DB update progress bar
-Bug fixes
-Nearly finalized DB format
-Some easy way to abort DB updating (right now you can click F12; I want to make the DB update button change into a cancel button)
-Add last updated date to DB, some other small DB changes
-Searching by item # / part of item #

TODO:
-Finish searching by item # (currently will mess up badly given a badly organized database)
-Write searching by name / partial name
-Item details
-min/max price fetchers (current price fetcher should be easily tweaked to this task)
-graph
-Fix graph gridlines
-Select & graph more than one item
-Speed enhancements
-More error catching


program JEW;
{
Author: Smartzkid
Date: 10/1/08
Last Revision: 10/5/08

TODO:
-Finish searching by item # (currently will mess up badly given a badly organized database)
-Write searching by name / partial name
-Item details
-min/max price fetchers (current price fetcher should be easily tweaked to this task)
-graph
-Fix graph gridlines
-Select & graph more than one item
-Speed enhancements
-More error catching
}
var
UpdatingDB : Boolean;
forceUPDBreak : Boolean;
EndInitForm : Boolean;
frmDesign : TForm;
Settings_BTN : TButton;
SearchBTN : TButton;
SearchTerm : TEdit;
ResultBox : TListBox;
SettingsBox : TGroupBox;
SETT_Label : TLabel;
SETT_DynSrch : TCheckBox;
SETT_ForceRecreateNumNameDB : TCheckBox;
SETT_UpdateDB : TButton;
SETT_ClosePanel : TButton;
SETT_DBLoc : TEdit;
SETT_DBUpdate : TProgressBar;
ITEM_DtlsPanel : TGroupBox;
Image1 : TImage;
ITEM_Label1 : TLabel;
MarketPrice : TLabel;
MinPrice : TLabel;
MaxPrice : TLabel;
OneWeek : TButton;
TwoWeeks : TButton;
OneMonth : TButton;
ThreeMonths : TButton;
CustomDays : TButton;

procedure InitFormOnClose(Sender : TObject; var Action : TCloseAction);
begin
if (not(frmDesign.ModalResult = 1)) then
EndInitForm := True;
end;

procedure ToggleSetting(section, setting, value1, value2: string);
//Toggles [section, setting] between value1 and value2. If [section, setting] is
// neither value1 or value2, it will be set to value1
begin
if(LoadSetting(section, setting) = value1)then
SaveSetting(section, setting, value2)
else
SaveSetting(section, setting, value1);
end;

procedure SaveSettingHandler(Sender: TObject);
begin
case Sender of
SETT_ClosePanel:
begin
SaveSetting('JEW', 'DynSrch', BoolToStr(Sett_DynSrch.Checked));
SaveSetting('JEW', 'DBLoc', SETT_DBLoc.Text);
end;
end;
end;

procedure OpenSettings(Sender: TObject);
begin
Settings_BTN.Enabled := false;
SettingsBox.Visible := true;
end;

procedure CloseSettings(Sender: TObject);
begin
Settings_BTN.Enabled := true;
SettingsBox.Visible := false;
SaveSettingHandler(Sender);
end;

function NameToNumber(name: String): integer;
begin
try
result := StrToInt(ReadINI('NumNameConvs', name, Sett_DBLoc.Text));
except
result := -1;
end;
end;

function NumberToNames(number: integer; FileData: String; var listbox: TListBox): String;
var
i, lastobj, results: integer;
t: string;
begin //TODO: Add max result #
lastobj := 1;
if(pos(inttostr(number), FileData) = 0)then
Exit;
repeat
for i := PosEx(inttostr(number), FileData, lastobj) downto 1 do
if(FileData[i] = #10)then
break;

t := Copy(FileData, i + 1, posex(inttostr(number), FileData, lastobj) - i);

t := Copy(t, 1, Pos('=', t) - 1);

lastobj := posex(inttostr(number), FileData, lastobj) + length(inttostr(number));

if(t <> '')then
begin
resultbox.items.add(t);
inc(results);
end;
until(posex(inttostr(number), FileData, lastobj) > pos('Created=Yuppers', FileData)) or (results > 50)
end;

procedure Search(Sender: TObject);
var
FileData, SearchItem, tmp: String;
File_Handle: Integer;
donesearch: boolean;
begin
if(SearchTerm.Text = '')then
Exit;

ResultBox.Items.Clear;

SearchItem := SearchTerm.Text;
try
File_Handle := OpenFile(SETT_DBLoc.Text, true);
if(not(ReadFileString(File_Handle, FileData, FileSize(File_Handle))))then
StrToInt('ERROR');
except
writeln('DB Access Error');
try
CloseFile(File_Handle);
except end;
TerminateScript;
end;

donesearch := true;

try
if(TrimNumbers(TrimOthers(SearchItem)) = '')then
NumberToNames(StrToInt(TrimOthers(SearchItem)), FileData, ResultBox);
except
donesearch := false;
writeln(ExceptionToString(ExceptionType, ExceptionParam));
end;
end;

function CurrentPrice(ObjNum: integer): integer;
var
tmpstr, Page: string;
Pos1, Pos2: integer;
tmpfloat: extended;
begin
Page := GetPage('http://itemdb-rs.runescape.com/viewitem.ws?obj='+inttostr(ObjNum));
Pos1 := Pos('>Market price:</b> ',Page);
Pos2 := PosEx('</span>',Page,Pos1);
tmpstr := Copy(Page,Pos1+19,Pos2-Pos1-20);
if pos('k', tmpstr)>0 then
begin
tmpfloat := StrToFloat(TrimLetters(tmpstr));
result := round(tmpfloat*1000);
end;
if pos('m', tmpstr)>0 then
begin
tmpfloat := StrToFloat(TrimLetters(tmpstr));
result := round(tmpfloat*1000000);
end;
tmpstr := TrimOthers(tmpstr);
if (pos('k', tmpstr) = 0)and(pos('m', tmpstr) = 0) then
result := strtoint(tmpstr);
Page := '';
end;

procedure SearchHandler(Sender: TObject);
begin
case Sender of
SearchTerm:
if(SETT_DynSrch.Checked)then
Search(Sender);
SearchBTN:
Search(Sender);
end;
end;

procedure ChangeInterval(Sender: TObject);
var
Interval: integer;
begin
OneWeek.Enabled := true;
TwoWeeks.Enabled := true;
OneMonth.Enabled := true;
ThreeMonths.Enabled := true;
CustomDays.Enabled := true;
CustomDays.Caption := 'Custom';

//Sender.Enabled := false? SCAR FAIL

case Sender of
OneWeek: OneWeek.Enabled := false;
TwoWeeks: TwoWeeks.Enabled := false;
OneMonth: OneMonth.Enabled := false;
ThreeMonths: ThreeMonths.Enabled := false;
CustomDays: CustomDays.Enabled := false;
end;

case Sender of
OneWeek: Interval := 7;
TwoWeeks: Interval := 14;
OneMonth: Interval := 31;
ThreeMonths: Interval := 31 * 3;
CustomDays:
begin
try
Interval := StrToInt(Readln('Interval (days)?'));
if(Interval < 0)then
Interval := 0 - Interval;
if(Interval = 0)then
Interval := 20;
CustomDays.Caption := IntToStr(Interval) + ' Days';
except
// CreateMessageDialog('Please enter a number', mrOk, mrOk);
Interval := 20;
CustomDays.Caption := IntToStr(Interval) + ' Days';
end;
end;
end;
end;

procedure UpdateDBUnsafe;
var
Page, ObjNum, ObjName, ObjPrice, DateStr: String;
Letter, PLP1, PLP2, ObjNum1, PageLimit,
ObjNum2, ObjName2, ObjPrice2, ObjPrice1,
time, t, t1, PageNumber, TotalItems :integer;
ObjPriceN :extended;
PageLimitSet, CreateNumNameConvs:boolean;
begin
if(not(ReadINI('NumNameConvs', 'Created', SETT_DBLoc.Text) = 'Yuppers'))then
CreateNumNameConvs := true;
if(SETT_ForceRecreateNumNameDB.Checked = true)then
CreateNumNameConvs := true;
try
TotalItems := StrToInt(ReadINI('LASTUPDATE', 'Date', SETT_DBLoc.Text));
except
TotalItems := 3201;
end;
time := getsystemtime;
DateStr := Replace(DateToStr(Date), '/', '_');
Letter:=1;
repeat
PageNumber:=1;
repeat
if chr(64+Letter) = 'Q' then
begin
Letter:=Letter+1;
break;
end;
Page := GetPage('http://itemdb-rs.runescape.com/results.ws?query=&sup=Initial%20Letter&cat='+chr(Letter+64)+'&sub=All&page='+inttostr(PageNumber)+'&vis=1&order=1&sortby=name&price=&members=');
if not PageLimitSet then
begin
//writeln('Setting PageLimit');
PLP1:=Pos('<td class="navright">',Page);
PLP2:=LastPosEx('ers=">',Page,PLP1);
if PLP2=-1 then
begin
PageLimit:=1;
PageLimitSet:=true;
end;
if PageLimitSet=false then
begin
PLP1:=PosEx('</a>',Page,PLP2);
PageLimit:=strtoint(Copy(Page,PLP2+6,PLP1-PLP2-6));
end;
PageLimitSet:=true;
// writeln(chr(letter+64)+' '+inttostr(pagelimit));
end;
t:=Pos('Change Today',Page);
repeat
ObjNum1:=PosEx('em.ws?obj=',Page,t);
if ObjNum1=-1 then
begin
Page:='';
//writeln('Last Item '+inttostr(PageNumber)+' '+inttostr(pagelimit));
break;
end;
ObjNum2:=PosEx('">',Page,ObjNum1);
ObjNum:=Copy(Page,ObjNum1+10,ObjNum2-ObjNum1-10);
ObjName2:=PosEx('</a',Page,ObjNum2);
ObjName:=Copy(Page,ObjNum2+3,ObjName2-ObjNum2-3);
ObjPrice1:=PosEx('<td>',Page,ObjName2);
ObjPrice2:=PosEx('</td>',Page,ObjPrice1);
ObjPrice:=Copy(Page,ObjPrice1+4,ObjPrice2-ObjPrice1-4);

ObjPriceN := 1;
if pos('k',ObjPrice)>0 then
ObjPriceN := 1000;
if pos('m',ObjPrice)>0 then
ObjPriceN := 1000000;
ObjPriceN := ObjPriceN * StrToFloat(TrimLetters(TrimOthers(ObjPrice)));

//Add item price to DB, recreate NumName DB if necessary
WriteINI(ObjNum, DateStr, FloatToStr(ObjPriceN), SETT_DBLoc.Text);
if(CreateNumNameConvs)then
WriteINI('NumNameConvs', ObjName, ObjNum, SETT_DBLoc.Text);

SETT_DBUpdate.Position := Round((t1 / TotalItems) * 100);
GetApplication.ProcessMessages;

t1:=t1+1;
t:=ObjName2;
until(forceUPDBreak)
PageNumber:=PageNumber+1;
if PageNumber>PageLimit then
begin
PageLimitSet:=false;
Letter:=Letter+1;
break;
end;
until(forceUPDBreak)
if Letter>26 then
break;
until(forceUPDBreak)
if(not(forceUPDBreak) and (GetSystemTime - time < 1000))then
begin
writeln('ERROR (' + IntToStr(GetSystemTime - time) + 'ms, impossible update time)');
SETT_UpdateDB.Caption := 'ERROR';
wait(500);
forceUPDBreak := true; //To make it not say that it was updated today
end else
writeln(inttostr(t1)+' items in '+inttostr(getsystemtime-time)+'ms');
WriteINI('TOTALITEMS', DateStr, inttostr(t1), SETT_DBLoc.Text);
if(not(forceUPDBreak))then
WriteINI('LASTUPDATE', 'Date', DateStr, SETT_DBLoc.Text);
if(CreateNumNameConvs)then
begin
WriteINI('NumNameConvs', 'Created', 'Yuppers', SETT_DBLoc.Text);
if(SETT_ForceRecreateNumNameDB.Checked = true)then
SETT_ForceRecreateNumNameDB.Checked := false;
end;
end;

procedure UpdateDB(Sender: TObject);
//TODO: Is this multithreadable? Or atleast 'pausable', such that the UI will be
//able to update every few seconds?
var
v : TVariantArray;
begin
if(not(UpdatingDB))then
begin
UpdatingDB := true;
SETT_UpdateDB.Caption := 'Cancel';
SETT_ClosePanel.Enabled := false;
SETT_DBUpdate.Enabled := true;
// SetArrayLength(v, 0);
// ThreadSafeCall('UpdateDBUnsafe', v);
UpdateDBUnsafe;
SETT_UpdateDB.Caption := 'DB Updated';
SETT_ClosePanel.Enabled := true;
SETT_DBUpdate.Enabled := false;
if(forceUPDBreak)then
begin
forceUPDBreak := false;
SETT_UpdateDB.Caption := 'Update DB';
end;
UpdatingDB := false;
end else
forceUPDBreak := true;
end;

// This form was parsed using DFM Form Parser v.26c Beta by Ron.
// Generated from GE_DB_Form.dfm.
procedure InitForm;
begin
frmDesign := CreateForm;
with frmDesign do
begin
OnClose := @InitFormOnClose;
Position := poScreenCenter;
BorderStyle := bsSingle;
BorderIcons := [biSystemMenu];
Width := 600;
Height := 373;
Caption := 'Grand Exchange Wizard (JEW)';
Color := clBtnFace;
Font.Color := clWindowText;
Font.Height := -11;
Font.Name := 'MS Sans Serif';
Font.Style := [];
PixelsPerInch := 96;
end;
Settings_BTN := TButton.Create(frmDesign);
with Settings_BTN do
begin
Parent := frmDesign;
Left := 212;
Top := 8;
Width := 48;
Height := 21;
Caption := 'Settings';
TabOrder := 0;
OnClick := @OpenSettings;
end;
SearchBTN := TButton.Create(frmDesign);
with SearchBTN do
begin
Parent := frmDesign;
Left := 166;
Top := 8;
Width := 44;
Height := 21;
Caption := 'Search';
TabOrder := 0;
OnClick := @SearchHandler;
end;
SearchTerm := TEdit.Create(frmDesign);
with SearchTerm do
begin
Parent := frmDesign;
Left := 8;
Top := 8;
Width := 155;
Height := 21;
TabOrder := 1;
Text := 'Search';
SearchTerm.OnChange := @SearchHandler;
SearchTerm.OnEnter := @SearchHandler;
end;
ResultBox := TListBox.Create(frmDesign);
with ResultBox do
begin
Parent := frmDesign;
Left := 8;
Top := 40;
Width := 249;
Height := 289;
ItemHeight := 13;
TabOrder := 2;
end;
SettingsBox := TGroupBox.Create(frmDesign);
with SettingsBox do
begin
Parent := frmDesign;
Left := 8;
Top := 8;
Width := 249;
Height := 321;
Caption := 'Settings';
TabOrder := 3;
Visible := False;
end;
SETT_Label := TLabel.Create(SettingsBox);
with SETT_Label do
begin
Parent := SettingsBox;
Left := 8;
Top := 48;
Width := 89;
Height := 13;
Caption := 'Database location:';
end;
SETT_DynSrch := TCheckBox.Create(SettingsBox);
with SETT_DynSrch do
begin
Parent := SettingsBox;
Left := 8;
Top := 16;
Width := 153;
Height := 17;
Caption := 'Search while typing query';
if(LoadSetting('JEW', 'DynSrch') = 'True')then
Checked := true
else
Checked := false;
TabOrder := 0;
end;
SETT_ClosePanel := TButton.Create(SettingsBox);
with SETT_ClosePanel do
begin
Parent := SettingsBox;
Left := 176;
Top := 296;
Width := 65;
Height := 17;
Caption := 'Save';
TabOrder := 1;
Onclick := @CloseSettings;
end;
SETT_DBLoc := TEdit.Create(SettingsBox);
with SETT_DBLoc do
begin
Parent := SettingsBox;
Left := 8;
Top := 64;
Width := 233;
Height := 21;
TabOrder := 2;
if(not(LoadSetting('JEW', 'DBLoc') = ''))then
Text := LoadSetting('JEW', 'DBLoc')
else
Text := Replace(GetApplication.ExeName, 'scar.exe', 'ItemDB\items.idb');
end;
SETT_UpdateDB := TButton.Create(SettingsBox);
with SETT_UpdateDB do
begin
Parent := SettingsBox;
Left := 8;
Top := 88;
Width := 65;
Height := 17;
Caption := 'Update DB';
TabOrder := 1;
OnClick := @UpdateDB;
end;
SETT_DBUpdate := TProgressBar.Create(SettingsBox);
with SETT_DBUpdate do
begin
Parent := SettingsBox;
Left := 77;
Top := 88;
Width := 164;
Height := 17;
Min := 0;
Max := 100;
Position := 0;
Enabled := false;
TabOrder := 0;
end;
SETT_ForceRecreateNumNameDB := TCheckBox.Create(SettingsBox);
with SETT_ForceRecreateNumNameDB do
begin
Parent := SettingsBox;
Left := 8;
Top := 107;
Width := 200;
Height := 17;
Caption := 'Force recreation of the NumName DB';
TabOrder := 0;
end;
ITEM_DtlsPanel := TGroupBox.Create(frmDesign);
with ITEM_DtlsPanel do
begin
Parent := frmDesign;
Left := 264;
Top := 8;
Width := 321;
Height := 321;
Caption := 'Item Details: (itemname) [ID]';
TabOrder := 4;
end;
Image1 := TImage.Create(ITEM_DtlsPanel);
with Image1 do
begin
Parent := ITEM_DtlsPanel;
Left := 8;
Top := 24;
Width := 305;
Height := 193;
end;
ITEM_Label1 := TLabel.Create(ITEM_DtlsPanel);
with ITEM_Label1 do
begin
Parent := ITEM_DtlsPanel;
Left := 8;
Top := 248;
Width := 33;
Height := 13;
Caption := 'Today:';
end;
MarketPrice := TLabel.Create(ITEM_DtlsPanel);
with MarketPrice do
begin
Parent := ITEM_DtlsPanel;
Left := 19;
Top := 264;
Width := 36;
Height := 13;
Caption := 'Market:';
end;
MinPrice := TLabel.Create(ITEM_DtlsPanel);
with MinPrice do
begin
Parent := ITEM_DtlsPanel;
Left := 35;
Top := 280;
Width := 20;
Height := 13;
Caption := 'Min:';
end;
MaxPrice := TLabel.Create(ITEM_DtlsPanel);
with MaxPrice do
begin
Parent := ITEM_DtlsPanel;
Left := 32;
Top := 296;
Width := 23;
Height := 13;
Caption := 'Max:';
end;
OneWeek := TButton.Create(ITEM_DtlsPanel);
with OneWeek do
begin
Parent := ITEM_DtlsPanel;
Left := 8;
Top := 224;
Width := 49;
Height := 17;
Caption := '1 Week';
TabOrder := 0;
OnClick := @ChangeInterval;
end;
TwoWeeks := TButton.Create(ITEM_DtlsPanel);
with TwoWeeks do
begin
Parent := ITEM_DtlsPanel;
Left := 64;
Top := 224;
Width := 49;
Height := 17;
Caption := '2 Weeks';
TabOrder := 1;
OnClick := @ChangeInterval;
end;
OneMonth := TButton.Create(ITEM_DtlsPanel);
with OneMonth do
begin
Parent := ITEM_DtlsPanel;
Left := 120;
Top := 224;
Width := 49;
Height := 17;
Caption := '1 Month';
TabOrder := 2;
OnClick := @ChangeInterval;
end;
ThreeMonths := TButton.Create(ITEM_DtlsPanel);
with ThreeMonths do
begin
Parent := ITEM_DtlsPanel;
Left := 176;
Top := 224;
Width := 57;
Height := 17;
Caption := '3 Months';
TabOrder := 3;
OnClick := @ChangeInterval;
end;
CustomDays := TButton.Create(ITEM_DtlsPanel);
with CustomDays do
begin
Parent := ITEM_DtlsPanel;
Left := 240;
Top := 224;
Width := 73;
Height := 17;
Caption := 'Custom';
TabOrder := 4;
OnClick := @ChangeInterval;
end;
end;

procedure SafeInitForm;
var
v : TVariantArray;
begin
SetArrayLength(v, 0);
ThreadSafeCall('InitForm', v);
end;

procedure ShowInitFormModal;
begin
frmDesign.ShowModal;
end;

procedure SafeShowInitFormModal;
var
v : TVariantArray;
begin
SetArrayLength(v, 0);
ThreadSafeCall('ShowInitFormModal', v);
end;

procedure MainInitForm;
begin
try
SafeInitForm;
SafeShowInitFormModal;
finally
FreeForm(frmDesign);
except
WriteLn('An error seems to have occurred in InitForm:');
Writeln(ExceptionToString(ExceptionType, ExceptionParam));
end;
end;

begin
ClearDebug;
GetSelf.WindowState := wsMinimized;
MainInitForm;
GetSelf.WindowState := wsNormal;
if (EndInitForm) then
TerminateScript;
end.


EDIT: http://www.srl-forums.com/forum/hidden-runescape-update-p473184.html#post473184

I guess this is somewhat useless now. I'm still gonna finish it, will probs release to all. Its been too long since I released something..

Smartzkid

2008-10-09 16:48:15

Finished the grapher, anyone have ideas for other uses? Haha what forum should I post in…new functions, other scripts, …?

program New;

function GraphPtsEx(
GraphPxlsX, GraphPxlsY: integer; //Size of the graph
zoom: integer; //Zoom
pts: TPointArray; //Graph points
GraphBCGColor, //Background color
GraphAxisColor, //Axis color
GraphLineColor: integer; //Line color
Axis_ForceVisible: boolean; //Force origin to be visible
connectPoints: boolean; //Draws lines connecting points
pointSize: integer; //Draws circles for each point (1 = disabled)
gridlines: boolean; //Draw gridlines
GridlineXSpacing, GridlineYSpacing, //Spacing between gridlines
GridlineColor: integer): integer; //Gridline color
var
x_min, x_max, y_min, y_max: integer;
i, temp: integer;
scaleX, scaleY: extended;
Graph_BMP, Result_BMP: integer;
graphPoints: TPointArray;
canvas: TCanvas;
begin
//Param-error catching
if(GraphPxlsX < 1)then
Exit;
if(GraphPxlsY < 1)then
Exit;
if(zoom < 1)then
zoom := 1;
if(Gridlines)then
begin
if(GridlineXSpacing < 1)then
GridlineXSpacing := 5;
if(GridlineYSpacing < 1)then
GridlineYSpacing := 5;
end;

//Create the bitmap that the graph will be displayed on and fill with a given color
Graph_BMP := BitmapFromString(GraphPxlsX, GraphPxlsY, '');
FastDrawClear(Graph_BMP, GraphBCGColor);

if(Axis_ForceVisible = false)then
begin
x_min := pts[0].x;
y_min := pts[0].y;
end;

//Find lowest and highest points
for i := 0 to high(pts) do
begin
if(pts[i].x < x_min)then
x_min := pts[i].x;
if(pts[i].x > x_max)then
x_max := pts[i].x;
if(pts[i].y < y_min)then
y_min := pts[i].y;
if(pts[i].y > y_max)then
y_max := pts[i].y;
end;

//add a 10% 'buffer' to the x axis
temp := round((abs(x_max) - x_min) / 10);
x_min := x_min - temp;
x_max := x_max + temp;

//add a 10% 'buffer' to the y axis
temp := round((abs(y_max) - y_min) / 10);
y_min := y_min - temp;
y_max := y_max + temp;

//Determine Graph-point to pixel scale
scaleX := GraphPxlsX / round(x_max - x_min);
scaleY := GraphPxlsY / round(y_max - y_min);

if(Gridlines)then
begin
//Draw vertical gridlines
for temp := 0 to round(GraphPxlsX / (GridlineXSpacing * ScaleX)) do
for i := 0 to GraphPxlsY - 1 do
FastSetPixel(Graph_BMP, round(GridlineXSpacing * temp * ScaleX) + (round(abs(x_min) * scaleX) mod GridlineXSpacing), i, GridlineColor);

//Draw horizontal gridlines
for temp := 0 to round(GraphPxlsY / (GridlineYSpacing * ScaleY)) - 1 do
for i := 0 to GraphPxlsX - 1 do
FastSetPixel(Graph_BMP, i, round(GridlineYSpacing * temp * ScaleY) + (round(((GraphPxlsY / ScaleY) + abs(y_min)) * scaleY) mod GridlineYSpacing), GridlineColor);
end;

//Draw vertical axis
if(x_min < 0)then
for i := 0 to GraphPxlsY - 1 do
FastSetPixel(Graph_BMP, round(abs(x_min) * scaleX), i, GraphAxisColor);

//Draw horizontal axis
if(y_min < 0)then
for i := 0 to GraphPxlsX - 1 do
FastSetPixel(Graph_BMP, i, round(((GraphPxlsY / ScaleY) - abs(y_min)) * scaleY), GraphAxisColor);


//Calculate the points
SetLength(graphPoints, Length(pts));
for i := 0 to high(pts) do
begin
graphPoints[i].X := round((pts[i].x - x_min) * scaleX);
graphPoints[i].Y := round(GraphPxlsY - ((pts[i].y - y_min) * scaleY));
end;

//Graph the points
for i := 0 to High(graphPoints) do
try
FastSetPixel(Graph_BMP, graphPoints[i].X, GraphPoints[i].Y, GraphLineColor);
except
writeln('Error at pt ' + inttostr(i + 1));
writeln(inttostr(graphPoints[i].X) + ', ' + inttostr(graphPoints[i].Y));
end;

if(connectPoints) or (pointSize > 1)then
canvas := GetBitmapCanvas(Graph_BMP);

if(connectPoints)then
canvas.Pen.Color := GraphLineColor; //Sets line color

if(pointSize > 1)then
canvas.Brush.Color := GraphLineColor; //Fills elipses with line color

if(connectPoints)then
for i := 0 to High(graphPoints) - 1 do
try
canvas.MoveTo(graphPoints[i].X, graphPoints[i].Y);
canvas.LineTo(graphPoints[i + 1].X, graphPoints[i + 1].Y);
except
writeln('Error drawing line between pts ' + inttostr(i) + ' and ' + inttostr(i + 1));
end;

if(pointSize > 1)then
for i := 0 to High(graphPoints) do
canvas.Ellipse(graphPoints[i].X - pointSize, graphPoints[i].Y - pointSize, graphPoints[i].X + pointSize, graphPoints[i].Y + pointSize);

Result_BMP := BitmapFromString(GraphPxlsX * zoom, GraphPxlsY * zoom, '');
SafeCopyCanvas(canvas, GetBitmapCanvas(Result_BMP), 0, 0, GraphPxlsX, GraphPxlsY, 0, 0, GraphPxlsX * zoom, GraphPxlsY * zoom)
FreeBitmap(Graph_BMP);

result := Result_BMP;
end;

//Displays graph with white background, brown axis, and black points
function GraphPts(GraphPxlsX, GraphPxlsY: integer; pts: TPointArray): integer;
begin
result := GraphPtsEx(GraphPxlsX, GraphPxlsY, 1, pts, 16777215, 5018076, 0, false, true, 2, true, 10, 10, 16701619);
end;

var
data: TPointArray;
graph: integer;

begin
data := [Point(1, 15), Point(2, 10), Point(3, 8), Point(4, 12), Point(5, 13), Point(6, 11), Point(7, 9)];
graph := GraphPtsEx(250, 250, 1, data, 16777215, 5018076, 0, false, true, 2, true, 1, 1, 16701619);
DisplayDebugImgWindow(250, 250);
SafeCopyCanvas(GetBitmapCanvas(graph), GetDebugCanvas, 0, 0, 250, 250, 0, 0, 250, 250);
FreeBitmap(Graph);
end.

EDIT:

GE Script TODOs:
-Find/fix bug in graphing
-Finish the item details page
-Separate NameNumberDB from the PriceDB
-Name searching
-Add ability to close item details page + auto open

Narcle

2008-10-09 18:54:21

Nice Smartkidz. Yeah I'm glad you took the project to heart, I got distracted with fighters and work/college etc.

I'll now look at it… and umm, technically is a RS script maybe Misc? idk

Edit:
Trying it out now. Its epic.
Should be in SRL-Members+ only.

Boreas

2008-10-13 23:24:54

http://www.villavu.com/forum/showthread.php?t=36683?p=481081#post481081

You guys thinking what I'm thinking?

pwnaz0r

2008-10-13 23:35:55

If you mean detecting best items to merchant, i sure hope so.

Narcle

2008-10-14 00:28:08

From: Boreas
http://www.villavu.com/forum/showthread.php?t=36683?p=481081#post481081

You guys thinking what I'm thinking?


That's why I wanted to have a program just for this. In depth and maybe even possible to predict if a item is going to rise or lower in price etc.

Boreas

2008-10-14 00:43:18

Cool. Have you been running the database creator everyday? If not I'll start, and will post a ListPossibleTechnicalTradeBuys after a while.

Narcle

2008-10-14 00:56:24

From: Boreas
Cool. Have you been running the database creator everyday? If not I'll start, and will post a ListPossibleTechnicalTradeBuys after a while.


No I haven't had time. Trying to get ready to move, get Fafsa done, get into college, prepare for work while still have some free time to do some scripting. Haven't had time to divulge into the merchanting thing again.

Hopefully once I'm settled and steady with new job I'll be able to mess with this again.

Edit:
Oh and bot WoW to get account prepared for selling gold. ;)

Boreas

2008-10-14 01:00:47

Ahh fun times.

pwnaz0r

2008-10-14 21:26:02

Here you go guys, this should update prices without you even needing to worry about it. updates Every 24 hours. Also hides (GetSelf.Hide), so not even on task manager.


program New;
const
DBLocation = 'C:\Program Files\SCAR 3.15\ItemDB\';

var
Page, ObjNum, ObjName, ObjPrice: String;

Letter, PLP1, PLP2, ObjNum1, PageLimit,
ObjNum2, ObjName2, ObjPrice2, ObjPrice1,
time, t, t1, PageNumber, theFile :integer;

ObjPriceN :extended;

PageLimitSet :boolean;


procedure PerformOp(Hide: boolean);
begin
if Hide then
begin
try
GetSelf.Hide;
except
Writeln('Couldn''nt hide SCAR.');
end;
end else
begin
try
GetSelf.Show;
except
Writeln('Couldn''nt show SCAR.');
end;
end;
end;

function ConvertTimeToStr(time: integer): string;
var
H, S, M : integer;
HS, MMS, SS: string;
begin
ConvertTime(Time, H, M, S);
if (H > 0)then
HS:= IntToStr(H) else Hs:= '00';
SS:= IntToStr(S);
if (S <= 9)then
SS:= '0'+IntToStr(s);
MMS:= IntToStr(M);
if (M <= 9)then
MMS:= '0'+IntToStr(M);
Result:= HS+':'+MMS+':'+SS;
end;


procedure WaitWhilstElse;
var
MyTime, I: integer;
begin
PerformOp(true);
MyTime:= GetSystemTime;
While ( Floor((GetSystemTime - MyTime) / 1000 / 60 / 60) < 24) do
for I:= 1 to 12 do
if IsFKeyDown(I)then
begin
PerformOp(false);
Writeln('Script Terminated by F Key at '+ConvertTimeToStr(GetSystemTime));
TerminateScript;
end;
PerformOp(false);
end;


procedure UpdatePrices;
begin
time:=getsystemtime;
theFile := RewriteFile(DBLocation + Replace(DateToStr(Date), '/', '_') + '.idb', false);
Letter:=1;
repeat
PageNumber:=1;
repeat
if chr(64+Letter) = 'Q' then
begin
Letter:=Letter+1;
break;
end;
Page := GetPage('http://itemdb-rs.runescape.com/results.ws?query=&sup=Initial%20Letter&cat='+chr(Letter+64)+'&sub=All&page='+inttostr(PageNumber)+'&vis=1&order=1&sortby=name&price=&members=');
if not PageLimitSet then
begin
//writeln('Setting PageLimit');
PLP1:=Pos('<td class="navright">',Page);
PLP2:=LastPosEx('ers=">',Page,PLP1);
if PLP2=-1 then
begin
PageLimit:=1;
PageLimitSet:=true;
end;
if PageLimitSet=false then
begin
PLP1:=PosEx('</a>',Page,PLP2);
PageLimit:=strtoint(Copy(Page,PLP2+6,PLP1-PLP2-6));
end;
PageLimitSet:=true;
// writeln(chr(letter+64)+' '+inttostr(pagelimit));
end;
t:=Pos('Change Today',Page);
repeat
ObjNum1:=PosEx('em.ws?obj=',Page,t);
if ObjNum1=-1 then
begin
Page:='';
//writeln('Last Item '+inttostr(PageNumber)+' '+inttostr(pagelimit));
break;
end;
ObjNum2:=PosEx('">',Page,ObjNum1);
ObjNum:=Copy(Page,ObjNum1+10,ObjNum2-ObjNum1-10);
ObjName2:=PosEx('</a',Page,ObjNum2);
ObjName:=Copy(Page,ObjNum2+3,ObjName2-ObjNum2-3);
ObjPrice1:=PosEx('<td>',Page,ObjName2);
ObjPrice2:=PosEx('</td>',Page,ObjPrice1);
ObjPrice:=Copy(Page,ObjPrice1+4,ObjPrice2-ObjPrice1-4);
if pos('k',ObjPrice)>0 then
begin
ObjPriceN:=StrToFloat(TrimLetters(ObjPrice));
ObjPriceN:=ObjPriceN*1000;
end;
if pos('m',ObjPrice)>0 then
begin
ObjPriceN:=StrToFloat(TrimLetters(ObjPrice));
ObjPriceN:=ObjPriceN*1000000;
end;
ObjPrice:=TrimOthers(ObjPrice);
if (pos('k',ObjPrice)=0)and(pos('m',ObjPrice)=0) then
ObjPriceN := strtofloat(ObjPrice);

//writeln(ObjNum+': '+ObjName+' '+floattostr(ObjPriceN));
WriteFileString(theFile, '<n>' + ObjName + '</n> ' + '<id>' + ObjNum + '</id> ' + '<gp>' + floatToStr(ObjPriceN) + '</gp>' + chr(10));
//Dump to file, send to database, etc
t1:=t1+1;
t:=ObjName2;
until isfkeydown(11);
PageNumber:=PageNumber+1;
if PageNumber>PageLimit then
begin
PageLimitSet:=false;
Letter:=Letter+1;
break;
end;
until isfkeydown(11);
if Letter>26 then
break;
until isfkeydown(11);
CloseFile(theFile);
writeln(inttostr(t1)+' items in '+ConvertTimeToStr(getsystemtime-time));
Writeln('This scan was completed on '+DateToStr(Date));
end;

procedure DoUpdate;
begin
PerformOp(false);
UpdatePrices;
PerformOp(true);
end;

procedure ScriptTerminate;
begin
PerformOp(false);
end;

begin
PerformOp(true);
repeat
until(Readln('Press any F key while its not searching to stop. Type OK to cont.') = Lowercase('ok'));
repeat
DoUpdate;
WaitWhilstElse;
until(false);
end.

Smartzkid

2008-10-14 21:47:42

Ahh, crap. I must not have uploaded the newest version..

Ooops. Well, its a small change, I'll fix it and post it *tomorrow

pwnaz0r

2008-10-14 21:58:22

Alrighty then :)

Wizzup?

2008-10-15 05:18:21

From: pwnaz0r
If you mean detecting best items to merchant, i sure hope so.


Not just detecting, I think he also meant automatically merchanting it. ;)

Boreas

2008-10-15 07:24:42

Not completely automatically, the user has to approve buy suggestions. Well, at least at first. If it's tweaked enough to never have a bad suggestion, then I suppose it could be complete automatic.

Bobarkinator

2008-10-15 15:33:25

You could set it up so that the user could input a price or something, and the script will sell when the item hits that price.

Smartzkid

2008-10-15 16:15:09

DB updater:

program Updater;
const
DontHide = true;

procedure PerformOp(Hide: boolean);
begin
if DontHide then
Exit;
if Hide then
begin
try
GetSelf.Hide;
except
Writeln('Couldn''nt hide SCAR.');
end;
end else
begin
try
GetSelf.Show;
except
Writeln('Couldn''nt show SCAR.');
end;
end;
end;

function ConvertTimeToStr(time: integer): string;
var
H, S, M : integer;
HS, MMS, SS: string;
begin
ConvertTime(Time, H, M, S);
if (H > 0)then
HS:= IntToStr(H) else Hs:= '00';
SS:= IntToStr(S);
if (S <= 9)then
SS:= '0'+IntToStr(s);
MMS:= IntToStr(M);
if (M <= 9)then
MMS:= '0'+IntToStr(M);
Result:= HS+':'+MMS+':'+SS;
end;


procedure WaitWhilstElse;
var
MyTime, I: integer;
begin
PerformOp(true);
MyTime:= GetSystemTime;
While ( Floor((GetSystemTime - MyTime) / 1000 / 60 / 60) < 24) do
for I:= 1 to 12 do
if IsFKeyDown(I)then
begin
PerformOp(false);
Writeln('Script Terminated by F Key at '+ConvertTimeToStr(GetSystemTime));
TerminateScript;
end;
PerformOp(false);
end;


procedure UpdatePrices;
var
Page, ObjNum, ObjName, ObjPrice, DateStr, DBPath: String;
Letter, PLP1, PLP2, ObjNum1, PageLimit,
ObjNum2, ObjName2, ObjPrice2, ObjPrice1,
time, t, t1, PageNumber, TotalItems :integer;
ObjPriceN :extended;
PageLimitSet, CreateNumNameConvs:boolean;
begin
if(not(LoadSetting('JEW', 'DBLoc') = ''))then
DBPath := ExtractFilePath(LoadSetting('JEW', 'DBLoc'))
else
DBPath := ExtractFilePath(Replace(GetApplication.ExeName, 'scar.exe', 'ItemDB\items.idb'));
if(not(ReadINI('NumNameConvs', 'Created', DBPath + 'numname.idb') = 'Yuppers'))then
CreateNumNameConvs := true;
try
TotalItems := StrToInt(ReadINI('LASTUPDATE', 'Date', DBPath + 'items.idb'));
except
TotalItems := 3201;
end;
time := getsystemtime;
DateStr := Replace(DateToStr(Date), '/', '_');
Letter:=1;
repeat
PageNumber:=1;
repeat
if chr(64+Letter) = 'Q' then
begin
Letter:=Letter+1;
break;
end;
Page := GetPage('http://itemdb-rs.runescape.com/results.ws?query=&sup=Initial%20Letter&cat='+chr(Letter+64)+'&sub=All&page='+inttostr(PageNumber)+'&vis=1&order=1&sortby=name&price=&members=');
if not PageLimitSet then
begin
//writeln('Setting PageLimit');
PLP1:=Pos('<td class="navright">',Page);
PLP2:=LastPosEx('ers=">',Page,PLP1);
if PLP2=-1 then
begin
PageLimit:=1;
PageLimitSet:=true;
end;
if PageLimitSet=false then
begin
PLP1:=PosEx('</a>',Page,PLP2);
PageLimit:=strtoint(Copy(Page,PLP2+6,PLP1-PLP2-6));
end;
PageLimitSet:=true;
// writeln(chr(letter+64)+' '+inttostr(pagelimit));
end;
t:=Pos('Change Today',Page);
repeat
ObjNum1:=PosEx('em.ws?obj=',Page,t);
if ObjNum1=-1 then
begin
Page:='';
//writeln('Last Item '+inttostr(PageNumber)+' '+inttostr(pagelimit));
break;
end;
ObjNum2:=PosEx('">',Page,ObjNum1);
ObjNum:=Copy(Page,ObjNum1+10,ObjNum2-ObjNum1-10);
ObjName2:=PosEx('</a',Page,ObjNum2);
ObjName:=Copy(Page,ObjNum2+3,ObjName2-ObjNum2-3);
ObjPrice1:=PosEx('<td>',Page,ObjName2);
ObjPrice2:=PosEx('</td>',Page,ObjPrice1);
ObjPrice:=Copy(Page,ObjPrice1+4,ObjPrice2-ObjPrice1-4);

ObjPriceN := 1;
if pos('k',ObjPrice)>0 then
ObjPriceN := 1000;
if pos('m',ObjPrice)>0 then
ObjPriceN := 1000000;
ObjPriceN := ObjPriceN * StrToFloat(TrimLetters(TrimOthers(ObjPrice)));

//Add item price to DB, recreate NumName DB if necessary
WriteINI(ObjNum, DateStr, FloatToStr(ObjPriceN), DBPath + 'items.idb');
if(CreateNumNameConvs)then
WriteINI('NumNameConvs', ObjName, ObjNum, DBPath + 'numname.idb');

if(t1 mod (TotalItems / 100) = 0)then
begin
ClearDebug;
writeln(inttostr(Round((t1 / TotalItems) * 100)) + '%');
end;

t1:=t1+1;
t:=ObjName2;
until(isFkeydown(12))
PageNumber:=PageNumber+1;
if PageNumber>PageLimit then
begin
PageLimitSet:=false;
Letter:=Letter+1;
break;
end;
until(isFkeydown(12))
if Letter>26 then
break;
until(isFkeydown(12))
if(isFkeydown(12) or (GetSystemTime - time < 1000))then
writeln('ERROR (' + IntToStr(GetSystemTime - time) + 'ms, impossible update time)')
else
begin
writeln(inttostr(t1)+' items in '+inttostr(getsystemtime-time)+'ms');
WriteINI('TOTALITEMS', DateStr, inttostr(t1), DBPath + 'items.idb');
WriteINI('LASTUPDATE', 'Date', DateStr, DBPath + 'items.idb');
if(CreateNumNameConvs)then
WriteINI('NumNameConvs', 'Created', 'Yuppers', DBPath + 'numname.idb');
end;
end;

procedure DoUpdate;
begin
PerformOp(false);
UpdatePrices;
PerformOp(true);
end;

procedure ScriptTerminate;
begin
PerformOp(false);
end;

begin
PerformOp(true);
repeat
until(uppercase(Readln('Press any F key while its not searching to stop. Type OK to cont.')) = 'OK');
repeat
DoUpdate;
WaitWhilstElse;
until(false);
end.

The Claw

2008-10-18 19:20:25

http://www.villavu.com/forum/showthread.php?t=36683?t=39502&highlight=merchant

This guy seems to have the right idea. If you put in a list of all the items you would consider buying, the script could update daily, do a few calculations, and show a list of items currently at their "buy points". Buy up when that happens, then when the script displays it being at their "sell points", sell. You could predict the buy and sell points using the script by keeping a record of how close the Daily and the 5 Day average are…when they're equal, or approaching a point - buy or sell. Definitely has potential

pwnaz0r

2008-10-18 19:27:21

Quick question: Instead of having to run the price updater, couldn't we just grab the numbers from somewhere on the webpage? or are those number server side

shaunthasheep

2008-10-18 20:02:50

its servserside

all the info is shown in an image (the graphs)
ill probally make a script to parse the graphs using the read text functions + getcolor functions and it shouldn't be that hard except the parts where the lines overlap.

pwnaz0r

2008-10-18 22:30:03

I dont think its possible to retrieve the 5 day and daily average price from the page, I looked through the code pretty well, actually i have been working with it quite alot and I looked for alot, and I dont think I saw it, idk. Maybe I'll look again for that, but it would be a hassle for some one to have to continue to update it with their computer every single day, so we would have some prices in the database, then send them to a database, or something, idk much about what you would do about sending it to the site, then uploading it, etc. It's not so hard, but its basically:

1. Is someone willing to run it at all times of the day to gather prices, since you can gather it from page, its server side.
2. Is someone willing to set up a database and host it somewhere, and etheir code it in scar recieving prices for 30 day, 7 day, and daily average or whatever.

I was trying some different things out today, grabbing things from the client, etc, but I can't get it to work very fast (like 1.5 seconds per item identification number), so if anyone could message me (pwnaz0r@hotmail.com) when I'm on msn, and help me out with that, that would be great. I've got some good idea's and some code done, just kinda need some help grabbing info from the HTML faster.

HyperSecret

2008-10-18 22:53:32

Well I could run something to get the prices constantly if you guys need to. If you can't tell my computer is on quite a bit. ;)

Narcle

2008-10-18 23:11:56

Actually I think there is a website that has been tracking GE prices since shortly after it started up. I'll call my friend and see if he knows/remembers the address.

Boreas

2008-10-19 00:41:42

From: The Claw
http://www.villavu.com/forum/showthread.php?t=36683?t=39502&highlight=merchant

This guy seems to have the right idea. If you put in a list of all the items you would consider buying, the script could update daily, do a few calculations, and show a list of items currently at their "buy points". Buy up when that happens, then when the script displays it being at their "sell points", sell. You could predict the buy and sell points using the script by keeping a record of how close the Daily and the 5 Day average are…when they're equal, or approaching a point - buy or sell. Definitely has potential


Even better. The script will look at every item, and tell you the ones approaching buy points, based on your chosen price min/max/range, categories, change rate, etc.



Anyone know how often jagex updates? Because it only takes about 5 mins to get the whole thing.

pwnaz0r

2008-10-19 01:03:48

By the way, anyone who doesn't understand and wants to (more about using grandexchange to merchant, http://www.youtube.com/results?search_query=grand+exchange+merchanting&search_type=&aq=0&oq=Grand+Exchange , and http://runescape.wikia.com/wiki/Grand_Exchange_Market_Watch

Narcle

2008-10-19 02:25:09

This reminds me of Ameri-Trade stock marketing software lol

The Claw

2008-10-19 08:51:14

From: pwnaz0r
I dont think its possible to retrieve the 5 day and daily average price from the page, I looked through the code pretty well, actually i have been working with it quite alot and I looked for alot, and I dont think I saw it, idk. Maybe I'll look again for that, but it would be a hassle for some one to have to continue to update it with their computer every single day, so we would have some prices in the database, then send them to a database, or something, idk much about what you would do about sending it to the site, then uploading it, etc. It's not so hard, but its basically:

1. Is someone willing to run it at all times of the day to gather prices, since you can gather it from page, its server side.
2. Is someone willing to set up a database and host it somewhere, and etheir code it in scar recieving prices for 30 day, 7 day, and daily average or whatever.

I was trying some different things out today, grabbing things from the client, etc, but I can't get it to work very fast (like 1.5 seconds per item identification number), so if anyone could message me (pwnaz0r@hotmail.com) when I'm on msn, and help me out with that, that would be great. I've got some good idea's and some code done, just kinda need some help grabbing info from the HTML faster.


Why not convert the script to PHP, chuck it on a webserver, have it automatically run once or twice a day? Then we could store it in a database, parse it, etc, all from the one place. And to protect it from outsiders we could have it authorised with a username/pass

pwnaz0r

2008-10-19 11:29:52

i mean, sounds good, but I personally don't know much php, so I didn't suggest it. Last time I looked at any php code was like 9 months ago. Guess I'll have to relearn or someone else will have to step up and write it.

Smartzkid

2008-10-19 13:03:15

Scar.exe -open AutoDBUpdate.scar -run

;)

Will keep you updated as long as you turn your computer on at least once a day

pwnaz0r

2008-10-19 13:06:45

Here's some code for SCAR, just wrote it in like 20 mins kinda sloppy but o well. If anyone wants to fix the AnalyzePrice procedure so it will actually add commas in the right spot, that would be great, don't have time to figure it out right now. I commented it out so it will just do the normal string;


program RSGE;

//Look at status to keep track of current items and time left.

const
QuicklyFind = false; // True = looks at 100 random uids.
// False = carefully analyzes every Item, finds best.
// Takes about an hour

type
GEItem = record
Name, Description: string;
Min, Max, Med: integer;
SP, TP: extended; //7 percent, 30 percent
end;

procedure GetCharNum(Character: Char);
var
I: integer;
begin
for I:= 0 to 127 do
if Character = Chr(I)then
begin
Writeln('Char is '+IntToStr(I));
exit;
end;
end;

function ConvertTimeToStr(time: integer): string;
var
H, S, M : integer;
HS, MMS, SS: string;
begin
ConvertTime(Time, H, M, S);
if (H > 0)then
HS:= IntToStr(H) else Hs:= '00';
SS:= IntToStr(S);
if (S <= 9)then
SS:= '0'+IntToStr(s);
MMS:= IntToStr(M);
if (M <= 9)then
MMS:= '0'+IntToStr(M);
Result:= HS+':'+MMS+':'+SS;
end;

function AnalyzePrices(Str: string): integer;
var
Rvariant: Variant;
begin
if (Pos('m', str) <> 0) then
begin
RVariant:= Copy(Str, 1, Pos('m', Str)-1);
result:= Round(RVariant * 1000000);
end else
try
result:= StrToInt(Str);
except
// Writeln('Please pick a Valid ID :)');
end;
end;

function AnalyzePercents(Str: string): extended;
var
RVariant: variant;
begin
if (Pos('drop', str) <> 0)then
RVariant:= Copy(Str, Pos('-', Str), High(Str)) else
RVariant:= Copy(Str, Pos('+', Str)+1, High(Str));
try
result:= RVariant;
except
end;
end;


function GrabIDInformation(ID: integer): GEItem;
var
Page, InfoBlock, MyString: string;
RVariant: variant;
begin
//ClearDebug;
Page:= GetPage('http://itemdb-rs.runescape.com/viewitem.ws?obj='+IntToStr(ID)+'&scale=0');
InfoBlock:= Copy(Page, Pos('<div class="row_b"><a href="results.ws?query=&amp;sup=Initial%20Letter&amp;sub=All&amp;cvis=1&amp;vis=0&amp;sortby=name&amp;order=1&amp;cat=Other">Other</a></div>', Page), Pos('Grand Exchange History for the Last 7 Days', Page));
result.Name:= Between('<div class="subsectionHeader">'+chr(10), chr(10)+'</div>', InfoBlock);
result.Description:= Between('alt="'+result.name+'">'+chr(10), '<br>', InfoBlock);
result.Min:= AnalyzePrices((Between('<b>Minimum price:</b> ', chr(10)+'</span>', InfoBlock)));
result.Max:= AnalyzePrices((Between('<b>Maximum price:</b> ', chr(10)+'</span>', InfoBlock)));
result.Med:= AnalyzePrices((Between('<b>Market price:</b> ', chr(10)+'</span>', InfoBlock)));
//Delete(InfoBlock, 0, Pos('7 Days:</b> <span class="', InfoBlock));
MyString:= Between('7 Days:</b> <span class="', '%</span>', InfoBlock);
RVariant:= AnalyzePercents(MyString);
result.SP:= RVariant;
MyString:= Between('30 Days:</b> <span class="', '%</span>', InfoBlock);
RVariant:= AnalyzePercents(MyString);
result.TP:= RVariant;
end;

function AddInStr(Pos: integer; Str, SubStr: string): string;
var
I: integer;
begin
result:= Str;
SetLength(result, Length(result)+1);
for I:= 1 To (Pos) do
result[i]:= result[(I+1)];
result[(Pos)]:= SubStr[1];
end;

function AdjPriceStr(Int: Integer): string;
var
L, I: integer;
begin
{result:= IntToStr(Int);
for I:=High(result) downto 1 do
begin
if ((I mod 3) = 0)then
Result:= AddInStr(I, result, ',');
end;}
result:= IntToStr(Int);
end;

procedure WriteGEItem(Item: GEItem);
var
ExtVars: array [0..1] of Variant;
begin
ExtVars[0]:= Item.SP;
ExtVars[1]:= Item.TP;
Writeln('Name : '+Item.Name);
Writeln('Description : '+Item.Description);
Writeln('Min Price : '+AdjPriceStr(Item.Min)+' gp.');
Writeln('Med Price : '+AdjPriceStr(Item.Med)+' gp.');
Writeln('Max Price : '+AdjPriceStr(Item.Max)+' gp.');
Writeln('7 Day Percent : '+ExtVars[0]+'%');
Writeln('30 Day Percent : '+ExtVars[1]+'%');
end;

procedure FindMerchantableItem(MinPrice: integer; MinPercent: extended);
var
I, L, ST: integer;
Item: GEItem;
begin
ST:= GetSystemTime;
ClearDebug;
Writeln('This could take a minute...');
L:= Randomrange(2500, 1);
for I:= L to (L + 100) do
begin
if (Item.SP > MinPercent) and (Item.med > MinPrice) then
begin
Writeln('Item ID : '+IntToStr(I));
WriteGEItem(Item);
exit;
end;
Status('Currently on item : '+IntToStr(I)+' | Estimated '+ConvertTimeToStr( Ceil((GetSystemTime-ST) / I) * (3201 - I)));
end;
Writeln('Could not find a merchantable item. Please retry');
end;

function GrabOnlySPandMed(ID: integer): GEItem;
var
Page, InfoBlock, MyString: string;
RVariant: variant;
begin
//ClearDebug;
Page:= GetPage('http://itemdb-rs.runescape.com/viewitem.ws?obj='+IntToStr(ID)+'&scale=0');
InfoBlock:= Copy(Page, Pos('<div class="row_b"><a href="results.ws?query=&amp;sup=Initial%20Letter&amp;sub=All&amp;cvis=1&amp;vis=0&amp;sortby=name&amp;order=1&amp;cat=Other">Other</a></div>', Page), Pos('Grand Exchange History for the Last 7 Days', Page));
//result.Name:= Between('<div class="subsectionHeader">'+chr(10), chr(10)+'</div>', InfoBlock);
//result.Description:= Between('alt="'+result.name+'">'+chr(10), '<br>', InfoBlock);
//result.Min:= AnalyzePrices((Between('<b>Minimum price:</b> ', chr(10)+'</span>', InfoBlock)));
//result.Max:= AnalyzePrices((Between('<b>Maximum price:</b> ', chr(10)+'</span>', InfoBlock)));
result.Med:= AnalyzePrices((Between('<b>Market price:</b> ', chr(10)+'</span>', InfoBlock)));
//Delete(InfoBlock, 0, Pos('7 Days:</b> <span class="', InfoBlock));
MyString:= Between('7 Days:</b> <span class="', '%</span>', InfoBlock);
RVariant:= AnalyzePercents(MyString);
result.SP:= RVariant;
//MyString:= Between('30 Days:</b> <span class="', '%</span>', InfoBlock);
//RVariant:= AnalyzePercents(MyString);
//result.TP:= RVariant;
end;

procedure FindMostMerchatableItem;
var
I, HighestValue, HighestID, ST: integer;
CItem: GEItem;
begin
ST:= GetSystemTime;
Writeln('This WILL take awhile...');
Writeln('');
Writeln('');
HighestValue:= 1;
HighestId:= 0;
ClearDebug;
For I:= 0 to 3201 do
begin
CItem.SP:= -0.98;
try
Status('Currently on item : '+IntToStr(I)+' | Estimated '+ConvertTimeToStr( Ceil((GetSystemTime-ST) / I) * (3201 - I)));
except
end;
CItem:= GrabOnlySPandMed(I);
if (CItem.SP = -0.98)then
begin
Writeln('Finished.');
exit;
end;
if ( (Round(CItem.SP * CItem.Med)) > (HighestValue) )then
begin
ClearDebug;
HighestValue:= (Round(CItem.SP * CItem.Med));
HighestID:= I;
try
Writeln('Current Highest ID: '+IntToStr(HighestID)+' | Value : '+IntToStr(HighestValue));
Writeln('http://itemdb-rs.runescape.com/viewitem.ws?obj='+IntToStr(HighestID));
except
end;
end;
end;
end;

begin
if (QuicklyFind)then
FindMerchantableItem(100, 10.0)
else FindMostMerchatableItem;
end.


EDIT: Now it finds a merchantable Item.

The Formula for suggesting seems to work. Here are some things it suggested…

http://itemdb-rs.runescape.com/viewitem.ws?obj=2011
http://itemdb-rs.runescape.com/viewitem.ws?obj=868
http://itemdb-rs.runescape.com/viewitem.ws?obj=1897
http://itemdb-rs.runescape.com/viewitem.ws?obj=2285

edit #2: Ok, updated it so that if you want to find the most merchantable item in Runescape, it predicts it (takes about an hour). I'm running right now will post the item when done, I really like the quick one better though, finds really good items in under a minute. :)

EDIT: So far its http://itemdb-rs.runescape.com/viewitem.ws?obj=181, which is a really good one.

Narcle

2008-10-19 14:28:51

From: The Claw
Why not convert the script to PHP, chuck it on a webserver, have it automatically run once or twice a day? Then we could store it in a database, parse it, etc, all from the one place. And to protect it from outsiders we could have it authorised with a username/pass


This is what I was thinking Claw. Problem is I don't know PHP well enough todo this… My friend could do it in a day but idk if i could get him to do this.

pwnaz0r

2008-10-19 15:23:58

Updated quite a bit, someone tell me how it works..


program RSGE;

//Look at status to keep track of current items and time left.

const
QuicklyFind = true; // True = looks at 100 random uids.
// False = carefully analyzes every Item, finds best.
// Takes about 2 hours, so run overnight.

type
GEItem = record
Name, Description: string;
Min, Max, Med: integer;
SP, TP: extended; //7 percent, 30 percent
end;

procedure GetCharNum(Character: Char);
var
I: integer;
begin
for I:= 0 to 127 do
if Character = Chr(I)then
begin
Writeln('Char is '+IntToStr(I));
exit;
end;
end;

function ConvertTimeToStr(time: integer): string;
var
H, S, M : integer;
HS, MMS, SS: string;
begin
ConvertTime(Time, H, M, S);
if (H > 0)then
HS:= IntToStr(H) else Hs:= '00';
SS:= IntToStr(S);
if (S <= 9)then
SS:= '0'+IntToStr(s);
MMS:= IntToStr(M);
if (M <= 9)then
MMS:= '0'+IntToStr(M);
Result:= HS+':'+MMS+':'+SS;
end;

function AnalyzePrices(Str: string): integer;
var
Rvariant: Variant;
begin
if (Pos('m', str) <> 0) then
begin
RVariant:= Copy(Str, 1, Pos('m', Str)-1);
result:= Round(RVariant * 1000000);
end else
try
result:= StrToInt(Str);
except
// Writeln('Please pick a Valid ID :)');
end;
end;

function AnalyzePercents(Str: string): extended;
var
RVariant: variant;
begin
if (Pos('drop', str) <> 0)then
RVariant:= Copy(Str, Pos('-', Str), High(Str)) else
RVariant:= Copy(Str, Pos('+', Str)+1, High(Str));
try
result:= RVariant;
except
end;
end;


function GrabIDInformation(ID: integer): GEItem;
var
Page, InfoBlock, MyString: string;
RVariant: variant;
begin
//ClearDebug;
Page:= GetPage('http://itemdb-rs.runescape.com/viewitem.ws?obj='+IntToStr(ID)+'&scale=0');
InfoBlock:= Copy(Page, Pos('<div class="row_b"><a href="results.ws?query=&amp;sup=Initial%20Letter&amp;sub=All&amp;cvis=1&amp;vis=0&amp;sortby=name&amp;order=1&amp;cat=Other">Other</a></div>', Page), Pos('Grand Exchange History for the Last 7 Days', Page));
result.Name:= Between('<div class="subsectionHeader">'+chr(10), chr(10)+'</div>', InfoBlock);
result.Description:= Between('alt="'+result.name+'">'+chr(10), '<br>', InfoBlock);
result.Min:= AnalyzePrices((Between('<b>Minimum price:</b> ', chr(10)+'</span>', InfoBlock)));
result.Max:= AnalyzePrices((Between('<b>Maximum price:</b> ', chr(10)+'</span>', InfoBlock)));
result.Med:= AnalyzePrices((Between('<b>Market price:</b> ', chr(10)+'</span>', InfoBlock)));
//Delete(InfoBlock, 0, Pos('7 Days:</b> <span class="', InfoBlock));
MyString:= Between('7 Days:</b> <span class="', '%</span>', InfoBlock);
RVariant:= AnalyzePercents(MyString);
result.SP:= RVariant;
MyString:= Between('30 Days:</b> <span class="', '%</span>', InfoBlock);
RVariant:= AnalyzePercents(MyString);
result.TP:= RVariant;
end;

function AddInStr(Pos: integer; Str, SubStr: string): string;
var
I: integer;
begin
result:= Str;
SetLength(result, Length(result)+1);
for I:= 1 To (Pos) do
result[i]:= result[(I+1)];
result[(Pos)]:= SubStr[1];
end;

function AdjPriceStr(Int: Integer): string;
var
L, I: integer;
begin
{result:= IntToStr(Int);
for I:=High(result) downto 1 do
begin
if ((I mod 3) = 0)then
Result:= AddInStr(I, result, ',');
end;}
result:= IntToStr(Int);
end;

procedure WriteGEItem(Item: GEItem);
var
ExtVars: array [0..1] of Variant;
begin
ExtVars[0]:= Item.SP;
ExtVars[1]:= Item.TP;
Writeln('Name : '+Item.Name);
Writeln('Description : '+Item.Description);
Writeln('Min Price : '+AdjPriceStr(Item.Min)+' gp.');
Writeln('Med Price : '+AdjPriceStr(Item.Med)+' gp.');
Writeln('Max Price : '+AdjPriceStr(Item.Max)+' gp.');
Writeln('7 Day Percent : '+ExtVars[0]+'%');
Writeln('30 Day Percent : '+ExtVars[1]+'%');
end;

procedure FindMerchantableItem(MinPrice: integer; MinPercent: extended);
var
I, L, ST: integer;
Item: GEItem;
begin
ST:= GetSystemTime;
ClearDebug;
Writeln('This could take a minute...');
L:= Randomrange(13500, 1);
for I:= L to (L + 100) do
begin
Item:= GrabIDInformation(I);
if (Item.SP > MinPercent) and (Item.med > MinPrice) then
begin
Writeln('Item ID : '+IntToStr(I));
WriteGEItem(Item);
Writeln('Gained '+IntToStr(Round((Item.SP * Item.Med) / 100))+' gp per in 7 days');
exit;
end;
Status('Currently on item : '+IntToStr(I)+' / '+intToStr(L+100));
end;
Writeln('Could not find a merchantable item. Please retry');
end;

function GrabOnlySPandMed(ID: integer): GEItem;
var
Page, InfoBlock, MyString: string;
RVariant: variant;
begin
//ClearDebug;
Page:= GetPage('http://itemdb-rs.runescape.com/viewitem.ws?obj='+IntToStr(ID)+'&scale=0');
InfoBlock:= Copy(Page, Pos('<div class="row_b"><a href="results.ws?query=&amp;sup=Initial%20Letter&amp;sub=All&amp;cvis=1&amp;vis=0&amp;sortby=name&amp;order=1&amp;cat=Other">Other</a></div>', Page), Pos('Grand Exchange History for the Last 7 Days', Page));
//result.Name:= Between('<div class="subsectionHeader">'+chr(10), chr(10)+'</div>', InfoBlock);
//result.Description:= Between('alt="'+result.name+'">'+chr(10), '<br>', InfoBlock);
//result.Min:= AnalyzePrices((Between('<b>Minimum price:</b> ', chr(10)+'</span>', InfoBlock)));
//result.Max:= AnalyzePrices((Between('<b>Maximum price:</b> ', chr(10)+'</span>', InfoBlock)));
result.Med:= AnalyzePrices((Between('<b>Market price:</b> ', chr(10)+'</span>', InfoBlock)));
//Delete(InfoBlock, 0, Pos('7 Days:</b> <span class="', InfoBlock));
MyString:= Between('7 Days:</b> <span class="', '%</span>', InfoBlock);
RVariant:= AnalyzePercents(MyString);
result.SP:= RVariant;
//MyString:= Between('30 Days:</b> <span class="', '%</span>', InfoBlock);
//RVariant:= AnalyzePercents(MyString);
//result.TP:= RVariant;
end;

procedure FindMostMerchatableItem;
var
I, HighestValue, HighestID, ST: integer;
CItem: GEItem;
begin
ST:= GetSystemTime;
Writeln('This WILL take awhile...');
Writeln('');
Writeln('');
HighestValue:= 1;
HighestId:= 0;
ClearDebug;
For I:= 0 to 13500 do
begin
CItem.SP:= -0.98;
try
Status('Currently on item : '+IntToStr(I)+' | Estimated '+ConvertTimeToStr( Ceil((GetSystemTime-ST) / I) * (3201 - I)));
except
end;
CItem:= GrabOnlySPandMed(I);
if (CItem.SP = -0.98)then
begin
Writeln('Finished on item' + IntToStr(I-1));
exit;
end;
if ( (Round(CItem.SP * CItem.Med)) > (HighestValue) )then
begin
ClearDebug;
HighestValue:= (Round((CItem.SP * CItem.Med) / 100));
HighestID:= I;
try
Writeln('Current Highest ID: '+IntToStr(HighestID)+' | Value : '+IntToStr(HighestValue));
Writeln('http://itemdb-rs.runescape.com/viewitem.ws?obj='+IntToStr(HighestID));
except
end;
end;
end;
end;

begin
if (QuicklyFind)then
FindMerchantableItem(100, 10.0)
else FindMostMerchatableItem;
end.

Narcle

2008-10-19 15:34:14

This could take a minute…
Item ID : 12176
Name : Spirit weed seed
Description : A seed for spirit weed.
Min Price : 151 gp.
Med Price : 158 gp.
Max Price : 165 gp.
7 Day Percent : 17.9%
30 Day Percent : 25.3%
Gained 28 gp per in 7 days
Successfully executed
Had to run it 3 times, but I finally found one.

The Claw

2008-10-19 16:58:20

This could take a minute…
Item ID : 1199
Name : Adamant kiteshield
Description : A large metal shield.

Min Price : 467 gp.
Med Price : 467 gp.
Max Price : 490 gp.
7 Day Percent : 11.4%
30 Day Percent : 18.3%
Gained 53 gp per in 7 days
Successfully executed


Which gives http://itemdb-rs.runescape.com/viewitem.ws?obj=1199. Great :) Once the impact of the new PvP worlds on the economy starts to lessen, this will be a great tool to have.

Edit - Don't have enough time to code anything myself for a few weeks, but how about advising on whether or not to sell an item? Have an array where the user can put in the ID's, and have the script check those items as well. Then have it report on the standard details and most importantly, if its approaching or has reached a sell point. If someone converted it to PHP, we could each have our own user account, which would enable us to log on to the server, and do all sorts of cool stuff - like the options to "watch" items, and have the script report back to us in a chart or something. Ultimately I'm thinking of a personalised space for each of us, which when we each log on to it shows a chart of items its recently reccommended to us, items we're watching, etc as a table.

Wizzup?

2008-10-20 17:10:56

Give me a few days, and I'll make a PHP / MySQL part… :)
EDIT: I hope.

pwnaz0r

2008-10-20 21:11:47

Ok, sounds good :). Seroko is on the job atm too :)

Boreas

2008-10-20 22:21:18

If were going with web-based, I have to ask, how public/private will this be?

Narcle

2008-10-20 22:33:23

I would try to keep this as private as possible. If to many people know about this data, its less chance of us actually being able to use it successfully.

pwnaz0r

2008-10-20 23:26:41

Yeah, we kinda already talked about it (me and Wizzup), keeping it private that is. Seroko is actually a very talented scripter and understands that it is meant to be private, I told him it was just between me and him, and he won't leak. It's fine, I think not many people know him and this might be a good chance for him to prove if he is SSRL material.

Boreas

2008-10-20 23:56:15

Fair enough.

BTW, I will leave out any mention of the GE in the upcoming Ultimate Item Database, but I will do it so that it can be easily integrated for you guys. I may put in a GetCurrentPrice, but only in a plugin in a way that usage can be restricted.

Which reminds me of a suggestion for pwnaz0r. Try modding your script to work with Smartzkid's item.idb. Should be a lot faster than tons of getpages.

Widget

2008-10-21 00:44:35

It'd be easy to keep it a secret to some point.

http://widget:pants@www.google.com/

Bobarkinator

2008-10-21 22:35:36

Yep, just tried Pwnaz0rs script and it definitely needs to be refined to PHP or something.

pwnaz0r

2008-10-21 23:10:13

Indeed, we are working on that Bob :)

Wizzup?

2008-11-03 18:59:59

So, what exactly needs to be done?
I have some time… And this project seems very interesting. I also have the moneys to test it. :p

The Claw

2008-11-04 03:33:43

From: Wizzup?
So, what exactly needs to be done?
I have some time… And this project seems very interesting. I also have the moneys to test it. :p


This:

From: The Claw
Which gives http://itemdb-rs.runescape.com/viewitem.ws?obj=1199. Great :) Once the impact of the new PvP worlds on the economy starts to lessen, this will be a great tool to have.

Edit - Don't have enough time to code anything myself for a few weeks, but how about advising on whether or not to sell an item? Have an array where the user can put in the ID's, and have the script check those items as well. Then have it report on the standard details and most importantly, if its approaching or has reached a sell point. If someone converted it to PHP, we could each have our own user account, which would enable us to log on to the server, and do all sorts of cool stuff - like the options to "watch" items, and have the script report back to us in a chart or something. Ultimately I'm thinking of a personalised space for each of us, which when we each log on to it shows a chart of items its recently reccommended to us, items we're watching, etc as a table.


And then, LEG IT!

tarajunky

2008-11-17 03:20:31

Any updates on this? :)

Lee Lok Hin

2008-11-17 04:07:39

I've tried parsing the graph, but i've hit a major setback: The image of the graph is in a .gif format, and we all know that SCAR only accepts Bitmaps. Shame. Anybody got a solution around this?

Narcle

2008-11-17 06:40:34

Has any PHP script been made to retrieve the information from Runescape server yet?

I take it we want to use this database and retrieve it with SCAR since that is the primary.

I was thinking of having the PHP script order them by the numbers (kinda obvious) and have them sorted in a INI or Text by Line format. SCAR then retrieves this page and sorts it out fairly fast that way. I'm guessing many were thinking of this way right?

Well I think a password could be added so no one can access it w/o a valid password/account through SCAR etc.

The big news is I think I'm moving again to a cheaper place to stay [Currently paying $400 for rent, moving to this other place it'll be $100/150] My PHP guru friend will be living there as well, I'm sure I can convince him to make a script like this, its very simple in his case. [Also I'll have less stress from getting money so I can script more :D]

Lee Lok Hin

2008-11-17 06:59:02

Great. Still need something for .gif though. Is it possible to use a plugin for that?

Narcle

2008-11-17 07:02:04

From: Lee Lok Hin
Great. Still need something for .gif though. Is it possible to use a plugin for that?


Thing is if you convert the .gif's will the color be correct for Bitmaps?

Lee Lok Hin

2008-11-17 07:04:17

Well, since i'm not going to display it on a debug image, it should be fine. If blue suddenly turns to purple, it will turn into purple throughout, so as long as I change that, it should be fine.

I'm trying to get the 30day chart, and analyse the number of pixels with an up change, compared to down change, and if downchange > 20 then invalid item.

Then see which one's up change is the highest. But just thought of a slight problem, overlapping lines. Hm. Well, shouldn't be too much of a problem.

Boreas

2008-11-17 11:46:31

I still think getting the text every day is easier than getting the picture every 30 days. And then from the raw data we can make graphs for any number of days.

nielsie95

2008-11-17 12:29:30

From: Lee Lok Hin
Great. Still need something for .gif though. Is it possible to use a plugin for that?


I made a script that downloads the pictures of all GE items. These were also in gif format, but SCAR was able to load/display them through loadbitmap.

munk

2008-11-17 15:37:37

From: nielsie95
I made a script that downloads the pictures of all GE items. These were also in gif format, but SCAR was able to load/display them through loadbitmap.


neat. You could also use PHPs GD2 library to convert it.

Narcle

2008-11-17 22:25:53

From: Boreas
I still think getting the text every day is easier than getting the picture every 30 days. And then from the raw data we can make graphs for any number of days.


That's what I was thinking. What time do they update the prices, isn't it like 2am Central time?

Markus

2008-11-18 10:05:39

Between 12.00 and 14.00 UTC they update them afaik

HyperSecret

2008-12-10 09:46:52

Is this project finished or still being worked on?

If the SRL Members do that Merchanting thing, we could use this to help with that.

R0b0t1

2008-12-19 23:46:06

To find the buy point accurately you'll actually only know about it a few days after it starts going up. If you just keep a text file up the prices and average it out into a trend, that should work. I'm actually doing something like this atm.

Ron

2008-12-28 00:38:09

This is a very good idea! I can't wait to see the finished product. I do like the idea of a web based GE tracker but I'm against a private one. If there is only a private version available, I would make a free one.