Skip to content

Instantly share code, notes, and snippets.

@iamcsharper
Created December 20, 2015 14:53

Revisions

  1. iamcsharper created this gist Dec 20, 2015.
    139 changes: 139 additions & 0 deletions BaseCollection.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,139 @@
    <?php
    abstract class BaseCollection
    {
    /**
    * rgDescriptions array
    * @var array
    */
    protected $items = [];

    /**
    * Each element is implementing ItemCollectionFilter!
    * @see ItemCollectionFilter
    * @var array
    */
    protected $filters = [];

    /**
    * @var ICollectionParser
    */
    protected $collectionParser;

    /**
    * @param array $items
    */
    public function __construct(array $items)
    {
    $this->items = $items;
    }

    public function getUniqueKey()
    {
    return md5(json_encode($this->items) . json_encode($this->filters));
    }

    /**
    * Returns {@link Item} array
    * @see Item
    * @return array
    */
    public function get()
    {
    return $this->collectionParser->parseCollection($this);
    }

    /**
    * Returns first row
    * @return mixed
    */
    public function first()
    {
    $this->filters[] = new SliceCollectionFilter(0, 1);

    return $this->get()[0];
    }

    public function add($value)
    {
    $this->items[] = $value;
    }

    public function getFilters()
    {
    return $this->filters;
    }

    // Bad code
    public function __call($name, $arguments)
    {
    if (strpos($name, 'findBy') !== false) {
    $string = strtolower(str_replace('findBy', '', $name));

    foreach ($this->items as $item) {
    if ($item[$string] == $arguments[0]) {
    return $this->collectionParser->parseItem($item);
    }
    }
    }
    }

    public function toArray()
    {
    return array_values($this->items);
    }

    public function toJSON()
    {
    return json_encode($this->toArray(), JSON_PRETTY_PRINT);
    }

    public function isEmpty()
    {
    return !$this->size();
    }

    public function size()
    {
    return count($this->items);
    }

    public function getItems()
    {
    return $this->items;
    }

    public function setItems(array $items)
    {
    $this->items = $items;
    }

    public function removeAt($i)
    {
    $index = 0;

    foreach ($this->items as $k => $v) {
    // Numeric or associative
    if ($index == $i || $k == $i) {
    unset($this->items[$k]);

    break;
    }

    ++$index;
    }
    }

    public function removeAtMany(array $ids)
    {
    $index = 0;

    foreach ($this->items as $k => $v) {
    // Numeric or associative
    if (in_array($index, $ids) || in_array($k, $ids)) {
    unset($this->items[$k]);
    }

    ++$index;
    }
    }
    }
    8 changes: 8 additions & 0 deletions BaseCollectionFilter.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,8 @@
    <?php
    interface BaseCollectionFilter {
    /**
    * @param BaseCollection $collection
    * @return array
    */
    public function execute(BaseCollection &$collection);
    }
    20 changes: 20 additions & 0 deletions CollectionMethods.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,20 @@
    <?php
    trait CollectionMethods {
    public function slice($a, $b)
    {
    $this->filters[] = new SliceCollectionFilter($a, $b);

    return $this;
    }

    /**
    * @param \Closure $closure
    * @return $this
    */
    public function where(\Closure $closure)
    {
    $this->filters[] = new WhereCollectionFilter($closure);

    return $this;
    }
    }
    241 changes: 241 additions & 0 deletions DotaItem.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,241 @@
    <?php
    class DotaItem extends Item
    {
    // Always is static for Dota items
    public static $contextId = 2;

    protected $appId = 570;

    protected static $dotaRaritiesEnum = [
    'common',
    'uncommon',
    'rare',
    'mythical',
    'legendary',
    'immortal',
    'arcana',
    'ancient'
    ];

    private static $dotaTypesEnum = [
    'Wearable',
    'HUD Skin',
    'Loading Screen',
    'Announcer',
    'Courier',
    'Tool',
    'Taunt',
    'Gem / Rune',
    'Bundle'
    ];

    private static $dotaQualitiesEnum = [
    'Infused',
    'Normal',
    'Auspicious',
    'Inscribed',
    'Heroic',
    'Genuine',
    'Cursed',
    'Frozen',
    'Unusual',
    'Elder',
    'Corrupted',
    'Autographed',
    'Exalted',
    'Legacy',
    'Ascendant',
    'Favored',
    ];

    private static $_heroes = [];

    public static function getHeroes()
    {
    // Get from steam, change these
    if (empty(self::$_heroes)) {
    $api = new APIBridge(config('steam-auth.api_key'));

    $json = $api->callApi('IEconDOTA2_570/', 'GetHeroes/v0001/', [
    'language' => 'russian'
    ]);

    $result = json_decode($json, true);

    $heroes = $result['result']['heroes'];

    self::$_heroes = $heroes;
    }

    return self::$_heroes;
    }

    private $rarity;
    private $quality;

    public function __construct(array $data)
    {
    parent::__construct($data);

    foreach ($this->tags as $tag) {
    if (strtolower($tag['category']) == 'rarity') {
    $this->rarity = strtolower($tag['name']);
    } elseif (strtolower($tag['category']) == 'hero') {
    $this->characters[] = $tag['name'];
    } elseif (strtolower($tag['category']) == 'quality') {
    $this->quality = $tag['name'];
    }
    }
    }

    public function getRarity()
    {
    return $this->rarity;
    }

    public function getHero()
    {
    return $this->characters[0];
    }

    private function getRarityOrder()
    {
    return self::$dotaRaritiesEnum[$this->getRarity()];
    }

    private static function checkBeforeSort($a, $b)
    {
    $badType = false;

    if (!($a instanceof DotaItem))
    $badType = gettype($a);
    if (!($b instanceof DotaItem))
    $badType = gettype($b);

    if ($badType)
    throw new \RuntimeException('Incompatible types. Excepted: ' . self::class . '. Got: ' . $badType);
    }

    /**
    * @param $a DotaItem
    * @param $b DotaItem
    * @return int
    */
    public static function sortByQuality($a, $b)
    {
    static::checkBeforeSort($a, $b);

    $al = (!isset(self::$dotaQualitiesEnum[$a->getQuality()])) ? 0 : self::$dotaQualitiesEnum[$a->getHero()];
    $bl = (!isset(self::$dotaQualitiesEnum[$b->getQuality()])) ? 0 : self::$dotaQualitiesEnum[$b->getHero()];

    if ($al == $bl)
    return 0;

    return ($al > $bl) ? -1 : 1;
    }

    /**
    * @param $a DotaItem
    * @param $b DotaItem
    * @return int
    */
    public static function sortByType($a, $b)
    {
    static::checkBeforeSort($a, $b);

    $al = self::$dotaTypesEnum[$a->getType()];

    $bl = self::$dotaTypesEnum[$b->getType()];

    if ($al == $bl) {
    return 0;
    }

    return ($al > $bl) ? -1 : 1;
    }

    /**
    * @param $a DotaItem
    * @param $b DotaItem
    * @return int
    */
    public static function sortByCharacter($a, $b)
    {
    static::checkBeforeSort($a, $b);

    $al = $bl = 0;

    foreach (self::getHeroes() as $hero) {
    if ($hero['localized_name'] == $a->getHero())
    $al = true;
    elseif ($hero['localized_name'] == $b->getHero())
    $bl = true;
    }

    if (!$al) {
    return 1;
    }

    if (!$bl) {
    return -1;
    }

    return strnatcmp($a->getHero(), $b->getHero());
    }

    /**
    * @param $a DotaItem
    * @param $b DotaItem
    * @return int
    */
    public static function sortByPrice($a, $b)
    {
    static::checkBeforeSort($a, $b);

    $al = $a->getPrice();
    $al = ($al) ? $al->getSitePrice() : 0;
    $bl = $b->getPrice();
    $bl = ($bl) ? $bl->getSitePrice() : 0;

    if ($al == $bl) {
    return 0;
    }

    return ($al > $bl) ? -1 : 1;
    }

    /**
    * @param $a DotaItem
    * @param $b DotaItem
    * @return int
    */
    public static function sortByRarity($a, $b)
    {
    static::checkBeforeSort($a, $b);

    $al = $a->getRarityOrder();
    $bl = $b->getRarityOrder();

    if ($al == $bl) {
    return 0;
    }

    return ($al > $bl) ? -1 : 1;
    }

    /**
    * @return mixed
    */
    public function getQuality()
    {
    return $this->quality;
    }

    /**
    * @param mixed $quality
    */
    public function setQuality($quality)
    {
    $this->quality = $quality;
    }
    }
    7 changes: 7 additions & 0 deletions ICollectionParser.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    <?php
    interface ICollectionParser
    {
    public function parseCollection(BaseCollection &$collection);

    public function parseItem($item);
    }
    321 changes: 321 additions & 0 deletions Item.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,321 @@
    <?php

    abstract class Item
    {
    public static $contextId;

    /**
    * Item name
    * @var string
    */
    protected $name;

    /**
    * #Color of the item
    * @var string
    */
    protected $nameColor;

    /**
    * Item's market name
    * @var string
    */
    protected $marketName;

    /**
    * id of the application, which item is added to
    * @var integer
    */
    protected $appId;

    /**
    * Item class id
    * @var integer
    */
    protected $classId;

    /**
    * Item instance id
    * @var integer
    */
    protected $instanceId;

    /**
    * Hash for url linked to to item's image
    * @see http://cdn.steamcommunity.com/economy/image/
    * @var string
    */
    protected $iconUrl;

    /**
    * Hash for url linked to to item's bigimage
    * @see http://cdn.steamcommunity.com/economy/image/
    * @var string
    */
    protected $iconUrlLarge;

    /**
    * Item type
    * @var string
    */
    protected $type;

    /**
    * Can we trade with it?
    * @var boolean
    */
    protected $tradable;

    /**
    * Can we sell this item on market?
    * @var boolean
    */
    protected $marketable;

    /**
    * Item's descriptions
    * @var array
    */
    protected $descriptions = [];

    /**
    * Item's tags
    * @var array
    */
    protected $tags = [];

    /**
    * Characters who can use this item
    * @var array
    */
    protected $characters = [];

    /**
    * Calculated in ItemPrice
    * @see ItemCollection
    * @var ItemPrice
    */
    protected $price;

    public function __construct(array $data)
    {
    $this->appId = $data['appid'];
    $this->classId = $data['classid'];
    $this->instanceId = $data['instanceid'];

    $this->iconUrl = $data['icon_url'];
    $this->iconUrlLarge = $data['icon_url_large'];

    $this->name = $data['name'];
    $this->marketName = $data['market_hash_name'];

    $this->nameColor = $data['name_color'];

    $this->tradable = $data['tradable'];
    $this->marketable = $data['marketable'];

    if (is_array($data['descriptions'])) {
    foreach ($data['descriptions'] as $description) {
    if (preg_replace('/\s+/', '', $description['value']) == '')
    continue;

    $this->descriptions[] = $description['value'];
    }
    }

    if (is_array($data['tags']))
    $this->tags = $data['tags'];

    foreach ($this->tags as $tag) {
    if (strtolower($tag['category_name']) == 'type') {
    $this->type = $tag['name'];
    }
    }

    if ($data['type'] != '')
    $this->type = $data['type'];
    }

    /**
    * @return string
    */
    public function getName()
    {
    return $this->name;
    }

    /**
    * @param string $name
    */
    public function setName($name)
    {
    $this->name = $name;
    }

    /**
    * @return string
    */
    public function getNameColor()
    {
    return $this->nameColor;
    }

    /**
    * @param string $nameColor
    */
    public function setNameColor($nameColor)
    {
    $this->nameColor = $nameColor;
    }

    /**
    * @return string
    */
    public function getMarketName()
    {
    return $this->marketName;
    }

    /**
    * @return int
    */
    public function getAppId()
    {
    return $this->appId;
    }

    /**
    * @return int
    */
    public function getClassId()
    {
    return $this->classId;
    }

    /**
    * @return int
    */
    public function getInstanceId()
    {
    return $this->instanceId;
    }

    /**
    * @return string
    */
    public function getIconUrl()
    {
    return $this->iconUrl;
    }

    /**
    * @return string
    */
    public function getIconUrlLarge()
    {
    return $this->iconUrlLarge;
    }

    /**
    * @return string
    */
    public function getType()
    {
    return $this->type;
    }

    /**
    * @return boolean
    */
    public function isTradable()
    {
    return $this->tradable;
    }

    /**
    * @return boolean
    */
    public function isMarketable()
    {
    return $this->marketable;
    }

    /**
    * @return array
    */
    public function getDescriptions()
    {
    return $this->descriptions;
    }

    /**
    * @return array
    */
    public function getTags()
    {
    return $this->tags;
    }

    /**
    * Link to a transparent png image of item
    * @return string
    */
    public function getClearUrl()
    {
    return 'https://steamcommunity-a.akamaihd.net/economy/image/class/' . $this->appId . '/' . $this->classId . '/';
    }

    /**
    * @return ItemPrice
    */
    public function getPrice()
    {
    return $this->price;
    }

    /**
    * @param ItemPrice $price
    */
    public function setPrice(ItemPrice $price)
    {
    $this->price = $price;
    }

    public function getSitePrice() {
    if (is_null($this->price))
    return 0;

    return $this->price->getSitePrice();
    }

    abstract public function getRarity();

    /*
    * Just to let children override all these methods
    */

    static public function sortByRarity($rarityA, $rarityB)
    {
    return 0;
    }

    static public function sortByQuality($a, $b)
    {
    return 0;
    }

    static public function sortByType($typeA, $typeB)
    {
    return 0;
    }

    static public function sortByCharacter($characterA, $characterB)
    {
    return 0;
    }

    static public function sortByPrice($priceA, $priceB)
    {
    return 0;
    }
    }
    37 changes: 37 additions & 0 deletions ItemCollection.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,37 @@
    <?php
    /**
    * That's it!
    **/

    class ItemCollection extends BaseCollection
    {
    use CollectionMethods;

    static $searches = [];

    public function __construct(array $items, ItemMarketPriceHelper $marketPriceHelper) {
    parent::__construct($items);

    $this->collectionParser = new ItemCollectionParser($marketPriceHelper);
    }

    public function __call($name, $arguments)
    {
    if (strpos($name, 'findBy') !== false) {
    $string = strtolower(str_replace('findBy', '', $name));

    if (!empty(self::$searches[$string]))
    return self::$searches[$string];

    foreach ($this->items as $item) {
    if ($item[$string] == $arguments[0]) {
    $parsed = $this->collectionParser->parseItem($item);

    self::$searches[$string] = $parsed;

    return $parsed;
    }
    }
    }
    }
    }
    74 changes: 74 additions & 0 deletions ItemCollectionParser.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,74 @@
    <?php
    class ItemCollectionParser implements ICollectionParser
    {
    private static $collectionCache;
    private static $itemCache;

    /**
    * @var ItemMarketPriceHelper
    */
    protected $priceHelper;

    public function __construct(ItemMarketPriceHelper $marketPriceHelper)
    {
    $this->priceHelper = $marketPriceHelper;
    }

    public function parseCollection(BaseCollection &$collection)
    {
    $key = $collection->getUniqueKey();

    if (isset(self::$collectionCache[$key]) && !empty(self::$collectionCache[$key]))
    return self::$collectionCache[$key];

    if ($collection->isEmpty())
    return [];

    /** @var BaseCollectionFilter $filter */
    foreach ($collection->getFilters() as $filter) {
    $filter->execute($collection);
    }

    $result = [];

    foreach ($collection->toArray() as $itemInfo) {
    $item = $this->parseItem($itemInfo);
    $result[] = $item;
    }

    self::$collectionCache[$key] = $result;

    return $result;
    }

    /**
    * @param $itemInfo
    * @return Item
    */
    public function parseItem($itemInfo)
    {
    if (empty($itemInfo) || !is_array($itemInfo))
    throw new \InvalidArgumentException('ItemInfo should be filled array!');

    $key = md5(json_encode($itemInfo));

    if (isset(self::$itemCache[$key]) && !empty(self::$itemCache[$key]))
    return self::$itemCache[$key];

    /** @var Item $item */
    $item = ItemStorage::createItem($itemInfo);

    if ($itemInfo['tradable'] !== '0') {
    try {
    /** @var $price ItemPrice */
    $price = $this->priceHelper->getItemPrice($item);

    $item->setPrice($price);
    } catch (\Exception $e) {
    \Log::debug($e->getMessage());
    }
    }

    return $item;
    }
    }
    92 changes: 92 additions & 0 deletions ItemMarketPriceHelper.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,92 @@
    <?php
    namespace App\Helpers\Steam;

    use App\Helpers\Steam\Items\Item;
    use App\Models\ItemPrice;
    use Cache;
    use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

    class ItemMarketPriceHelper
    {
    // protected $communityURL = 'http://anonymouse.org/cgi-bin/anon-www.cgi/http://steamcommunity.com/';
    protected $communityURL = 'http://steamcommunity.com/';
    protected $apiKey;

    /**
    * @var int
    */
    protected $currency;

    /**
    * @param $apiKey
    * @param int $currency
    */
    public function __construct($apiKey, $currency = 5)
    {
    $this->apiKey = $apiKey;
    $this->currency = $currency;
    }

    protected function cleanFloat($str)
    {
    $float = (float)str_replace(',', '.', preg_replace('/[\s]*/', '', $str));
    return $float;
    }

    protected function cleanInt($str)
    {
    return (int)str_replace(',', '.', preg_replace('/[\s]*/', '', $str));
    }

    public function setCurrency($currency)
    {
    $this->currency = $currency;
    }

    /**
    * @param $item Item
    * @return ItemPrice
    */
    public function getItemPrice($item)
    {
    $params = [
    'currency' => $this->currency,
    'appid' => $item->getAppId(),
    'market_hash_name' => $item->getMarketName()
    ];

    $query = http_build_query($params);

    $price = ItemPrice::
    where('class_id', $item->getClassId())
    ->where('app_id', $item->getAppId())
    ->first();

    if (!$price) {
    $url = $this->communityURL . 'market/priceoverview/?' . $query;

    $contents = @file_get_contents($url);
    $result = json_decode($contents, true);

    if (!$result['success']) {
    throw new NotFoundHttpException('Стим считает нас дудосерами по ссылке ' . $url);
    }

    $lowest = (isset($result['lowest_price'])) ? $this->cleanFloat($result['lowest_price']) : 0;
    $median = (isset($result['median_price'])) ? $this->cleanFloat($result['median_price']) : 0;
    $volume = (isset($result['volume'])) ? $this->cleanInt($result['volume']) : 0;

    $cached = [
    'class_id' => $item->getClassId(),
    'app_id' => $item->getAppId(),
    'min' => $lowest,
    'median' => $median,
    'volume' => $volume,
    ];

    $price = ItemPrice::create($cached);
    }

    return $price;
    }
    }
    42 changes: 42 additions & 0 deletions ItemStorage.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,42 @@
    <?php

    class ItemStorage
    {
    private static $relations = [
    '570' => DotaItem::class,
    ];

    public static function getItemClass($appId)
    {
    $appId = $appId . '';

    if (!isset(self::$relations[$appId])) {
    throw new \InvalidArgumentException('Theres no item for id: ' . $appId);
    }

    return self::$relations[$appId];
    }

    public static function getItemContextID($appId)
    {
    $className = self::getItemClass($appId);
    $prop = 'contextId';

    if (!property_exists($className, $prop))
    throw new \RuntimeException('Item called ' . $className . ' hasnt ContextID! Fix it!');

    $vars = get_class_vars($className);
    return $vars[$prop];
    }

    /**
    * @param $itemInfo
    * @return mixed
    */
    public static function createItem($itemInfo)
    {
    $item = self::getItemClass($itemInfo['appid']);
    /* @var $item Item */
    return new $item($itemInfo);
    }
    }
    43 changes: 43 additions & 0 deletions SliceCollectionFilter.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,43 @@
    <?php
    class SliceCollectionFilter implements BaseCollectionFilter
    {
    /**
    * @var int
    */
    private $from;

    /**
    * @var int
    */
    private $to;

    /**
    * SliceCollectionFilter constructor.
    * @param int $from
    * @param int $to
    */
    public function __construct($from, $to)
    {
    $this->from = max(0, $from);
    $this->to = (int)$to;
    }

    public function execute(BaseCollection &$collection)
    {
    $items = $collection->toArray();
    $bad = [];

    foreach ($items as $i => $v) {
    if ($i < $this->from || $i >= $this->to) {
    $bad[] = $i;
    }
    }

    $collection->removeAtMany($bad);
    }

    public function __toString()
    {
    return 'Slice';
    }
    }
    39 changes: 39 additions & 0 deletions WhereCollectionFilter.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,39 @@
    <?php
    class WhereCollectionFilter implements BaseCollectionFilter
    {
    /**
    * @var \Closure
    */
    private $closure;

    /**
    * WhereCollectionFilter constructor.
    * @param \Closure $closure
    */
    public function __construct(\Closure &$closure)
    {
    $this->closure = &$closure;
    }

    public function execute(BaseCollection &$collection)
    {
    $bad = [];

    /* @var $closure \Closure */
    $closure = $this->closure;
    $items = $collection->toArray();

    foreach ($items as $i => $v) {
    if (!$closure($v)) {
    $bad[] = $i;
    }
    }

    $collection->removeAtMany($bad);
    }

    public function __toString()
    {
    return 'Where';
    }
    }