Giter Club home page Giter Club logo

hexgate's Introduction

Hexgate

Discord Codacy Badge Release License npm (scoped)

Hexgate is a work-in-progress LCU suite. It is not endorsed by Riot Games. You can find out more about what that means here. Thank you Riot ❤️ for keeping the LCU open. If you have any questions, feel free to join the cuppachino discord.

Please refer to the wiki for more info.

Installation

Add it to your own project using your favorite package manager.

pnpm add hexgate
npm i hexgate
yarn add hexgate

ESM

import { ... } from "hexgate"

CJS

import hexgate = require("hexgate")
const { ... } = hexgate

Authentication

Wait for the client by passing the auth function to the poll utility.

import { auth, poll } from "hexgate"

const credentials = await poll(auth)

Opt-out of safe authentication by explicity passing an undefined certifcate.

const unsafeCredentials = await auth({ certificate: undefined })

Once you have the credentials, you can create a new Hexgate and LcuClient.

import { Hexgate as HttpsClient, LcuClient as WsClient } from "hexgate"

const httpsClient = new HttpsClient(credentials)
const websocketClient = new WsClient(credentials)

Working with multiple clients? Get get all credentials.

import { auth, createHexgate, createLcuClient, poll, zip } from 'hexgate'

const credentials = await poll(() => auth({ all: true }))

// ~ some multi-connection interface
const clients = new Set(
  zip(
    credentials.map(createHexgate),
    credentials.map(createLcuClient)
  )
)

Builder API

The simplest way of getting started is to ".build" a request function. The builder uses generics to infer the parameters and return type of the request.

import { Hexgate as HttpsClient } from 'hexgate'

const https = new HttpsClient(credentials)

// (arg: string[], init?: any) => Promise<ApiResponse<{ ... }>>
const getSummonersFromNames = https
  .build('/lol-summoner/v2/summoners/names')
  .method('post')
  .create()

const summoner = await getSummonersByName(['dubbleignite'])
console.log(summoner.data)

Websocket Events

Subscribe to LCU events through the client.

import { LcuClient as WsClient } from 'hexgate'

const ws = new WsClient(credentials)

ws.subscribe(
  'OnJsonApiEvent_lol-champ-select_v1_session',
  ({ data, eventType, uri }) => {
    // side effects
  }
)

Note: Since many endpoints will subscribe you to multiple uris, its difficult to provide meaningful type inference for the data property. Import LcuComponents type when necessary and/or open a PR to improve typings - which would be greatly appreciated! I'm just improving types as I need them.

⚡️ Connection

The Connection class further abstracts Hexgate & LcuClient and handles authentication between client shutdowns. Configuration is optional.

import { Connection } from 'hexgate'

const client = new Connection({
  // Recipe API (createRecipe or recipe)
  createRecipe({ build, unwrap }) {
    return {
      getCurrentSummoner: unwrap(
        build('/lol-summoner/v1/current-summoner').method('get').create()
      )
    }
  },
  // Propagate status to browser windows.
  onStatusChange(status) {
    emit('status', status)
  },
  // Init
  async onConnect(con) {
    con.ws.subscribe('OnJsonApiEvent_lol-champ-select_v1_session', handleChampSelect)
    const summoner = await con.recipe.getCurrentSummoner()
    con.logger.info(summoner, `Welcome, ${summoner.displayName}`)
  },
  // Automatically reconnect
  async onDisconnect(discon) {
    await sleep(4000)
    discon.connect()
  },
  // Authentication interval
  interval: 2000,
  // Bring any logger
  logger: pino({
    name: 'main' as const
  })
})

client.connect()

The Connection class supports recipes, define a recipe: Recipe or a createRecipe: RecipeApiFn method in the ConnectionConfig constructor argument.

import { Connection, createRecipe } from 'hexgate'

const recipe = createRecipe(({ build }) => ({/*...*/}))
const client = new Connection({
  recipe
})
import { Connection } from 'hexgate'

const client = new Connection({
  createRecipe({ build }) { return {/*...*/} }
})

Recipe API

createRecipe is a higher-order function for transforming a request's parameters and response. It is a useful tool for morphing the LCU's API into your own. There are several ways to use the functions provided by the callback, and we'll take a look at each one.

Intro

Step 1: Create a recipe

This is identical to the builder API, except the request function isn't built until a hexgate instance is given to the recipe. This is useful for modeling requests ahead of time for usage in other places.

import { createRecipe } from "hexgate"

/**
 * <T extends HttpsClient>(httpsClient: T) => 
 *   (arg: string[], init?: RequestInit) => 
 *     Promise<ApiResponse<{...}>>
 */
const getSummonersFromNamesRecipe = createRecipe(({ build }) =>
  build('/lol-summoner/v2/summoners/names')
    .method('post')
    .create()
)

Step 2: Once you have a recipe, you just need to pass it a Hexgate.

const getSummonersFromNames = getSummonersFromNamesRecipe(httpsClient)

const summoners = await getSummonersFromNames(['dubbleignite'])
console.table(summoners.data)

🦋 Transforming requests

Use wrap, from, to, and unwrap to design your api.

const summonersRecipe = createRecipe(({ build, wrap, from, to, unwrap }) => ({
  getSummoners: {
    /**
     * Default for reference.
     * (arg: { ids?: string; }, init?: RequestInit) => Promise<ApiResponse<{...}>>
     */
    v2SummonersDefault: build('/lol-summoner/v2/summoners')
      .method('get')
      .create(),

    /**
     * unwrap extracts the data property from an ApiResponse.
     * (arg: { ids?: string }, init?: RequestInit) => Promise<{...}>
     */
    v2SummonersAwaited: unwrap(
      build('/lol-summoner/v2/summoners').method('get').create(),
    ),

    /**
     * wrap let's us overwrite the parameters type by supplying conversion functions.
     * (summonerIds: (number | `${number}`)[], init?: RequestInit | undefined) => Promise<{...}>
     */
    fromSummonerIds: wrap(
      build('/lol-summoner/v2/summoners').method('get').create(),
    )({
      // The return type of `from` is constrained by the expected return type of the function being wrapped.
      from(summonerIds: Array<`${number}` | number>, init?) {
        return [{ ids: JSON.stringify(summonerIds) }, init];
      },
      // awaits data similarly to `unwrap`
      to,
    }),
  },
}));

⚒️ Recipe, RecipeApiFn, and CreateWithRecipe

Some features have options that accept a Recipe, the product of createRecipe, or a RecipeApiFn, the api argument expected by createRecipe. You can achieve similar functionality in your own code by extending CreateWithRecipe or implementing its overloaded constructor signature.

import type { CreateWithRecipe } from 'hexgate'

class Foo<T> extends CreateWithRecipe<T> {}
new Foo(recipe)
new Foo((recipeApi) => "your recipe" as const)

Exporting recipes

If you want to export a recipe, you might get a type error. This is because the return type of createRecipe is inferred with references to @cuppachino/openapi-fetch and node-fetch-commonjs. To fix this, install the packages as dev dependencies and apply one of the following solutions to your tsconfig.json:

Map paths (Recommended)

Use this option if you are making a library.

{
  "compilerOptions": {
    "paths": {
      "@cuppachino/openapi-fetch": ["./node_modules/@cuppachino/openapi-fetch"],
      "node-fetch-commonjs": ["./node_modules/node-fetch-commonjs"]
    }
  }
}

Add types to the global scope (apps)

This can be used in applications, but it's not recommended.

{
  "compilerOptions": {
    "types": ["@cuppachino/openapi-fetch", "node-fetch-commonjs"]
  }
}

Additional features

LcuValue

The LcuValue class implements Update and CreateWithRecipe. It's useful for caching data retrieved from the LCU.

import { Connection, LcuValue, type OperationResponses } from 'hexgate'

type LolOwnedChampionsMinimal =
  OperationResponses<'GetLolChampionsV1OwnedChampionsMinimal'>

class ChampionLookup extends LcuValue<LolOwnedChampionsMinimal> {
  constructor() {
    super(({ build, unwrap }) =>
      unwrap(
        build('/lol-champions/v1/owned-champions-minimal')
          .method('get')
          .create()
      )
    )
  }

  championById(id: string | number | undefined) {
    return this.inner?.find((c) => c.id === Number(id ?? 0))
  }
}

const champions = new ChampionLookup()

const client = new Connection({
  async onConnect(con) {
    await champions.update(con.https)
    con.logger.info(
    champions.championById(1) satisfies
        | Partial<LolOwnedChampionsMinimal>[number]
    )
  }
})

client.connect()

Development

This package uses pnpm to manage dependencies. If you don't have pnpm, it can be installed globally using npm, yarn, brew, or scoop, as well as some other options. Check out the pnpm documentation for more information.

pnpm i

hexgate's People

Contributors

codacy-badger avatar cuppachino avatar dependabot[bot] avatar github-actions[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

hexgate's Issues

Can't locate LeagueClientUx Process (LINUX)

Seems like the process name changed from LeagueClientUx -> LeagueClient.ex.

Currently we're using ps -A | grep LeagueClientUx and we need to use ps -Af | grep LeagueClient.ex

Needs documentation

I'm still adding features to the project but didn't want to leave out an example of making requests. This guide still needs to be written out.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.