Giter Club home page Giter Club logo

twitch-cli's Issues

Breaking Change: change _every_ eventsub "argument" to use the name of the eventsub

For example

event trugger

uses streamup or streamdown

So I have to check the documentation every time to find the topic I need rather than invoking stream.online and stream.offline from the eventsub docs.

So change/update twitch-cli to use ONLY the eventsub "true" names as per the eventsub docs and/or alias the eventsub names to the twitch-cli names.

I mean stream-change instead of channel.update is just convoluted!

And with the death of websub we can ditch all the websub bits from the cli anyway

prediction events outcomes should include "users" and "channel_points" fields

Currently, prediction outcomes for prediction-progress and prediction-end (prediction-lock doesn't work) events do not include "users" and "channel_points" fields as specified in EventSub reference.

Generated outcome data for prediction-progress / prediction-end events:

{
    "id":"4b6b53c2-762f-7d81-df54-0f3a8c428f3e",
    "title":"yes",
    "color":"blue",
    "top_predictors": [ ... ]
}

Expected outcome data:

{
    "id":"4b6b53c2-762f-7d81-df54-0f3a8c428f3e",
    "title":"yes",
    "color":"blue",
    "users": 4,
    "channel_points": 12208,
    "top_predictors": [ ... ]
}

Possible to do a eventsub-subscription?

https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types#channelupdate

There is some documentation, but I'm struggling to understand how to setup a channel.update subscription to my callback URL using this twitch-cli.

I can get broadcaster details as a one time thing twitch api get channels -q broadcaster_id=9679595 , but to get it to happen automatically via the callback url, I'm not sure.

Is there somewhere with examples of how I should translate the dev.twitch.tv api page into something that'll work with this cli command.
I'm trying to migrate my old WebSub code to use this cli, but a bit lost. Any push in the right direction would be greatly appreciated.

Typo in mock-api docs

POST /token
grant_type example is incorrect ?grant_type=user_token
should be ?grant_type=client_credentials

event trigger hype-train-end does not include id field in event

Hi, I'm super new to using the twitch API so maybe I did something wrong but I'm trying to design a duplicate check in my application that listens for the hype-train-end eventsub. The twitch cli does a great job simulating it, however when using the command twitch event trigger hype-train-end the JSON that is sent to my server for webhook testing does not include an id property within the event property. However according to the EventSub reference the id property should be within the event property so that consumers of the webhook can check for duplicates. See: https://dev.twitch.tv/docs/eventsub/eventsub-reference/#hype-train-end-event

mock-api: /auth/token should support body params

My applications currently uses body params as per Section 2.3.1 of RFC 6749 for app access tokens.

curl -X POST "https://id.twitch.tv/oauth2/token" --data "grant_type=client_credentials&client_id=myclientid&client_secret=myclientsecret" --header "content-type: application/x-www-form-urlencoded"

This is not supported with mock-api "/auth" endpoints.

curl -X POST "http://localhost:8080/auth/token" --data "grant_type=client_credentials&client_id=myclientid&client_secret=myclientsecret" --header "content-type: application/x-www-form-urlencoded"

returns an empty body

It should be easy for me to migrate to the non-RFC query variant, but could this be implemented too?

Configure command doesn't accept valid client secret

Issue

The current client secret validation only accepts secrets with a length of exactly 30 characters. However, my valid secret has 31.
It's worth mentioning that my application is one of the so-called old applications. Maybe those are (or were) able to generate secrets with variable length.

Current workaround

Configure the CLI with a random 30 character secret and edit the resulting .twitch-cli.env file manually afterwards.

Triggered subscribe event sends broadcast_ instead of broadcaster_

When triggering the subscribe event, it generates the following payload

{
  "subscription": {
    "id": "5311fcca-d097-f8f6-502e-38153349f4d9",
    "type": "channels.subscribe",
    "version": "test",
    "condition": {
      "broadcaster_user_id": "44314184"
    },
    "transport": {
      "method": "eventsub",
      "callback": "null"
    },
    "created_at": "2021-01-04T14:14:34Z"
  },
  "event": {
    "user_id": "7177807",
    "user_name": "testFromuser",
    "broadcast_user_id": "44314184",
    "broadcast_user_name": "testBroadcaster"
  }
}

In the event, it should be broadcaster_user_id and broadcaster_user_name according to the Channel Subscribe Event
docs.

twitch-cli should use standard config directory

Hi,

Right now, twitch-cli is using $HOME/.twitch-cli directory as its default config path. It is kind of anoying, putting another directory in the home of the user.
At least under Linux, it should use the now quite common directory $HOME/.config/ with a subdir in it : $HOME/.config/twitch-cli/ .

What do you think ?

Fix tarbomb.

When extracting the Linux .tgz file from the releases page, it is a tarbomb. This can be very detrimental to anyone extracting the application.

A tarbomb, also sometimes written as tar bomb, is a tarball whose contents appear to explode into the current directory or some other existing directory containing a large number of items when untarred rather than into a new directory created by the tarball specifically for such contents.

Hypetrain events use version "1.0" whereas other events use version "1"

I'm testing various eventsub subscriptions on a chatbot I'm making and I noticed that whereas most events have a version of "1", hype-train-begin (and possibly the other hype-train events) have a version of "1.0" which causes typing issues in some languages (such as python).

{"subscription":{"id":"5f8deb7b-4ff3-4032-ddfb-62f190eb79d2","status":"enabled","type":"channel.hype_train.begin","version":"1.0",...

vs

{"subscription":{"id":"f8b4f5b3-cd32-4a60-d052-473d85fed1d2","status":"enabled","type":"channel.cheer","version":"1",...

mock-api: wrong content-type

twitch-cli version: twitch-cli/source (9370e1a)

curl -X GET "http://localhost:8080/units/categories" --header "accept: application/json" -v

sends data with header Content-Type: text/plain; charset=utf-8

curl -X POST "http://localhost:8080/auth/token?client_id=mockid&client_secret=mocksecret&grant_type=client_credentials" --header "Accept: application/json" -v

also sends data with header Content-Type: text/plain; charset=utf-8

curl.exe -X GET "http://localhost:8080/mock/users" --header "Accept: application/json" --header "Authorization: Bearer mytoken" --header "Client-ID: mymockid" -v

returns Content-Type: application/json correctly

Error/warn on invalid secret

According to https://dev.twitch.tv/docs/eventsub#secret, an eventsub secret should be between 10-100 characters.

It would be nice/helpful if the Twitch CLI provided an error if the provided secret is invalid (outside of this range).

Example, I'd expect the following to provide an error rather than a successful JSON mock:

twitch event trigger hype-train-progress --secret '2short' -F https://endpoint
twitch event trigger hype-train-progress --secret 'thisisareallylongsecretthisisareallylongsecretthisisareallylongsecretthisisareallylongsecretthisisareallylongsecret' -F https://endpoint

Pretty Print disables when used

Expected Behavior

When using the --pretty-print argument - it should stay enabled and output something like this:

{
  "data": [
    {
      "broadcaster_type": "affiliate",
      "created_at": "2018-10-01T15:26:44.113951Z",
      "description": "The Alt-F4 Stream is an idea long time friends AT0TA and Blackglasses had of being able to game/stream together miles apart. We are real life friends that met long ago in a little town in Florida and kept our friendship over the years through video games. Find out more about us below!",
      "display_name": "TheAltF4Stream",
     ....
    }
  ]
}

Actual Behavior

It appears to be disabling the flag when used instead of keeping it enabled rendering non-pretty output:

{"data":[{"id":"264030156","login":"thealtf4stream","display_name":"TheAltF4Stream","type":"","broadcaster_type":"affiliate","description":"The Alt-F4 Stream is an idea long time friends AT0TA and Blackglasses had of being able to game/stream together miles apart. We are real life friends that met long ago in a little town in Florida and kept our friendship over the years through video games. Find out more about us below!","profile_image_url":"https://static-cdn.jtvnw.net/jtv_user_pictures/ffedad62-cc6e-497f-8eef-61e7fec4c9b4-profile_image-300x300.png","offline_image_url":"https://static-cdn.jtvnw.net/jtv_user_pictures/ce008d9e-a92f-4781-a2d0-4bba41061812-channel_offline_image-1920x1080.png","view_count":95898,"created_at":"2018-10-01T15:26:44.113951Z"}]}

Steps to Reproduce the Problem

  1. Use the following command as an example and make sure --pretty-print is included.
twitch-cli api --pretty-print get users -q login=thealtf4stream
  1. Should return a non-pretty output of your query.

Specifications

  • Version: Beta Version 0.1.0
  • Platform: Linux

Extend verify-subscription to support --to-user/etc

Some people will add code to their verify subscription routines to only allow unexpected "user ID's" to verify.

Currently verify-subscription doesn't support any of the other flags to control the condition. So it's not possible to run a verify-subscription test for a specfic condition it's randomy every time,.

verify-subscription sends wrong value for Twitch-Eventsub-Message-Type header

It looks like the new verify-subscription event is sending "notification" as the value for the Twitch-Eventsub-Message-Type header. It should be "webhook_callback_verification". I check this value in my route and respond with the challenge if it's "webhook_callback_verification". This is causing the CLI command to fail for my endpoint.

"Use my payload"

For eventsub it would be useful to do one or more of the following:

Provide a default "base payload" that contains some (but not all) keys.

So if I pass in:

{
        "broadcaster_user_id": "1337",
        "broadcaster_user_login": "cool_user",
        "broadcaster_user_name": "Cool_User",
}

A "test" of channel.update would overwrite the keys in

https://github.com/twitchdev/twitch-cli/blob/main/internal/events/types/stream_change/stream_change_event.go#L68

with the keys from the JSON above.

The JSON, could be a "default" JSON file/blob specified in the config.
or at run time either via < pass in or --file argument
--file could also logically be a configuration variable.

or

twitch event configure broadcaster_user_id x

Would define a broadcaster_user_id to use always until I unset

twitch event unconfigure broadcaster_user_id

-f would still override a preconfigure

Further to this a

twitch event signsend EVENTYPE < somefile.json

Would also suffice.

Use Case:

Say for example I am testing my endpoint for message types for a specific streamer, I don't want to have to type in/specify the broadcasterID every time (and the login/name being correct is a +)

Or I would chain a twitch-cli api users call into an eventsub call to populate the broadcaster (after suitable command line schnanaigans to provide the accurate user_login/name from an ID)

Notes:

This is a bit rambly and I need to refine this feature request a little.

Nested arrays in API responses

When performing any API request, data array in returned body contains unnecessary array inside with the content, which I believe shouldn't be a case.
Expected behavior is to have the response looking as close to the official API response, with just one array of objects.

Example (tcurl is an alias for curl <headers with my credentials> $@):
forsen in the background

I'd want the data array in response of twitch-cli, to look just like data array in the API call in the example shown above.

Schedule endpoints error

Any call to the new Schedule endpoints returns:
Error unmarshalling body: json: cannot unmarshal object into Go struct field APIResponse.data of type []interface {}

schedule icalendar endpoint returns:
Error unmarshalling body: invalid character โ€˜Bโ€™ looking for beginning of value.

Similar errors across all the /schedule endpoints.

Error was present and consistent in 1.0 and 1.0.2 builds.

New events related to channel subscription messages and gifts

Will there be support for triggering the new events for Channel Subscription Gift and Channel Subscription Message, which were just moved out of public beta and into v1 this past month (June 11 and June 15 respectively)?

https://dev.twitch.tv/docs/change-log
https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types/#channelsubscriptiongift
https://dev.twitch.tv/docs/eventsub/eventsub-subscription-types/#channelsubscriptionmessage

eventsub: unban too many fields

A live unban event doesn't include reason/is_permanent

just the three users involved.

So a unban event on the CLI shouldn't send

				Reason:               "This is a test event",
				EndsAt:               util.GetTimestamp().Format(time.RFC3339Nano),
				IsPermanent:          params.IsPermanent,

Reason: "This is a test event",

Initially not sure how to fix :-D

Token command not generating all scopes

Im having an issue with the token command.

twitch token -u -s bits:read chat:read chat:edit channel:edit:commercial channel:manage:broadcast channel:read:hype_train channel:read:subscriptions moderation:read user:edit user:manage:blocked_users user:read:blocked_users user:read:email user:read:follows user:read:subscriptions user:read:email user:read:follows user:read:subscriptions

When i examine the url generated by twitch-cli it only contains the bits:read scope. Its almost like the parser isnt parsing the whole string. It generates the following url:
id.twitch.tv/oauth2/authorize?client_id=[redacted]&redirect_uri=http%3A%2F%2Flocalhost%3A3000&response_type=code&scope=bits%3Aread&state=[redacted]

localhost
Ive attached a screen shot.
regards,
Lewis

[mock-api] [auth/authorize] [400] [Invalid scopes requested]

I have been getting the following response after setting scope param

{
    "status": 400,
    "error": "Bad Request",
    "message": "Invalid scopes requested"
}

Example request:
http://localhost:8001/auth/authorize?grant_type=user_token&client_id=17d1b5987fb4c50e1f17a8cab565fb&client_secret=ba59c5c116f25510d010992d0cbe13&user_id=59532795&scope=chat:edit

ItemId doesn't get passed to Redemption

According to the help flags you can specify a custom item ID for the add-redemption event:

$ twitch event trigger add-redemption -h
...
-i, --item-id string           Manually set the ID of the event payload item (for example the reward ID in redemption events).

You can then do this like so: twitch event trigger add-redemption -i random-id-here

However, the generated body doesn't include this ID, and instead uses a randomly generated one:

{
   ...
   "event":{
      ...
      "reward":{
         "id":"65f6f018-006b-be0b-fa1e-01ae9c7f177a"
         ...
      }
   }
}

I've had a quick look through the command definition and how it gets passed through to the RedemptionParams structure, but nothing obvious is jumping out at me.

Mock Update Reward endpoint validates incorrectly

The mock api Update Reward has incorrect validation of the incoming patch body, namely:

  • Requires Cost to be specified here
    • Should allow cost to be nil/unspecified, which would mean cost would not be updated
  • If StreamMaxEnabled is true, and StreamMaxCount is 0, errors here
    • Technically correct, but might be worth considering the record when the value of StreamMaxCount is nil as a zero value there in the case of a true StreamMaxEnabled should cause a 400
  • Similarly for StreamUserMaxEnabled and GlobalCooldownEnabled and their 'values'

Currently the simple update below fails due to the cost being required, but will work on the live API.

{ "is_enabled": true } 

Workaround right now is to ensure the cost is on the update in order to validate, but it's something that my client doesn't have interest in knowing so it's something that I have to only supply against the mock, but not on the real call.

Total stripped from Get Users Follows

 twitch api get 'users/follows?to_id=15185913&first=1'
{
  "data": [
    {
      "followed_at": "REDACTED",
      "from_id": "REDACTED",
      "from_login": "REDACTED",
      "from_name": "REDACTED",
      "to_id": "15185913",
      "to_login": "barrycarlyon",
      "to_name": "BarryCarlyon"
    }
  ],
  "pagination": {
    "cursor": "eyJiIjpudWxsLCJhIjp7IkN1cnNvciI6ImV5SjBjQ0k2SW5WelpYSTZNVGM1TXpJNE1EQTVPbVp2Ykd4dmQzTWlMQ0owY3lJNkluVnpaWEk2TVRVeE9EVTVNVE1pTENKcGNDSTZJblZ6WlhJNk1UVXhPRFU1TVRNNlptOXNiRzkzWldSZllua2lMQ0pwY3lJNklqRTJNell4TXpVMU5UTXdOemN3TlRZME1URWlmUT09In19"
  }
}

The documented total field seems to have removed the total field.
Not sure if this effects other API calls currently.

Release for macOS ARM

Would it be possible to also release this for the new M1 macs? I was able to install it using the Silicon version of Homebrew until 0.6.0. I see the formula is explicitly checking for Mac and Intel now.

Erroneous twitch-evensub-subscription-type

When receiving mock events, I noticed that all channel event types are misspelled in the response headers:

'twitch-eventsub-subscription-type': 'channels.subscribe'

but shouldn't it be:

'twitch-eventsub-subscription-type': 'channel.subscribe',

(channels.<type> should be channel.<type> without the trailing s behind channel.)

Misspelled variable name

This one's really minor, but I figured there's no reason to not report it. The word "parameters" is frequently misspelled as "paramaters" in a number of identifiers. Some quick grepping tells me this is found in internal/api/api.go, internal/events/trigger_event.go, internal/events/retrigger_event.go and cmd/events.go.

Calling bottle :unneeded is deprecated!

Warning: Calling bottle :unneeded is deprecated! There is no replacement.
Please report this issue to the twitchdev/twitch tap (not Homebrew/brew or Homebrew/core):
/usr/local/Homebrew/Library/Taps/twitchdev/homebrew-twitch/Formula/twitch-cli.rb:10

mock-api: event_type and event_timestamp is swapped

I tried to fix this myself, but having trouble understanding how the sql is used and my knowledge of go is minimal.

The issue is that mock/moderation/banned/events and mock/moderation/moderators/events endpoints return wrong data

image
image

Allow for custom subscription id in verify-subscription command

twitch event verify-subscription currently does not allow a subscription id to be passed with -i, unlike most other event commands.

I don't imagine this would be used much, but it would be useful for me as I use the verification request to inform my application that the subscription exists, and update the database as such. Being able to specify an id for the subscription being verified would make my testing more robust, since I would not have to manually edit the database to confirm that the subscription has been verified, instead the webhook handler can perform the update as it usually would.

Next expected release/nightly builds available?

I have two questions concerning releases:

  1. When can the next build be expected?
  2. Is there some (official) place to get nightly builds or something like that?

I started setting up Go just for self compiling but ran into quite some issues. And before I bite myself through I just wanted to make sure that there is not already something better available.

Discover existing/new clients easily

Currently the only way to get a client_id and client_secret is to either search through the mock-api generate output, or look into the clients table in the database.

twitch mock-api generate | rg -U "Client-ID: (.*)\nSecret: (.*)" -r '$1,$2'

or

sqlite3 /path/to/.twitch-cli/eventCache.db -csv "SELECT id,secret FROM clients LIMIT 1;"

Would it be reasonable to provide a /units endpoint to grab all existing clients and a mock-api clients --limit x command? Date of creation should probably be added if that happens

Another nice thing would be to have an option for mock-api generate to dump in json what client was generated and to omit generating a client with a flag.

Missing attributes when triggering subscription event

When I trigger a subscription event via the event trigger command there are only four attributes...

$ twitch event trigger subscribe -t 265345534
{
    "subscription": {
        ...
    },
    "event": {
        "user_id": "47333490",
        "user_name": "testFromuser",
        "broadcaster_user_id": "265345534",
        "broadcaster_user_name": "testBroadcaster"
    }
}

... whereas the event should have eight attributes according to the documentation. The attributes

  • user_login,
  • broadcaster_user_login,
  • tier and
  • is_gift

are currently missing.

Hypetrain Begin and Progress events do not contain Progress datum

According to the Twitch API, the Hype Train Begin event contains the following fields:

"event": {
        "id": "1b0AsbInCHZW2SQFQkCzqN07Ib2",
        "broadcaster_user_id": "1337",
        "broadcaster_user_login": "cool_user",
        "broadcaster_user_name": "Cool_User",
        "total": 137,
        "progress": 137,
        "goal": 500,
        "top_contributions": [
            { "user_id": "123", "user_login": "pogchamp", "user_name": "PogChamp", "type": "bits", "total": 50 },
            { "user_id": "456", "user_login": "kappa", "user_name": "Kappa", "type": "subscription", "total": 45 }
        ],
        "last_contribution": { "user_id": "123", "user_login": "pogchamp", "user_name": "PogChamp", "type": "bits", "total": 50 },
        "started_at": "2020-07-15T17:16:03.17106713Z",
        "expires_at": "2020-07-15T17:16:11.17106713Z"
    }

When I sent myself a test event, these were the fields:

"event": {
        "id":"704d800a-da5f-6d47-5881-4836908c0440",
        "broadcaster_user_id":"74852038",
        "broadcaster_user_login":"testBroadcaster",
        "broadcaster_user_name":"testBroadcaster",
        "total":420,
        "goal":69295,
        "top_contributions": [
            {"total":686,"type":"bits","user_id":"75248508","user_name":"cli_user1","user_login":"cli_user1"},
            {"total":533,"type":"subscription","user_id":"34060829","user_name":"cli_user2","user_login":"cli_user2"}
        ],
        "last_contribution":{"total":533,"type":"subscription","user_id":"34060829","user_name":"cli_user2","user_login":"cli_user2"},
        "started_at":"2021-12-15T20:20:24.4014103Z",
        "expires_at":"2021-12-15T20:25:24.4014103Z"
    }

As you can see, "progress" is missing.

Extend Configure for EventSub Defaults

For a user testing eventsub they would have to -F and -s every command

It would be useful for a developer to define a default, potentially via the configure command

So when doing a lot of eventsub testing, a developer doesn't have to specify -F and -s everytime since it's gonna be easy to fatfinger the -s and/or copy paste all the time.

Also nicer for people on shell only to test.

Unable to install via Homebrew after 1.1.3

Tried to upgrade to 1.1.3 using brew and received the following.

==> Upgrading twitchdev/twitch/twitch-cli
  1.1.1 -> 1.1.3

Warning: A newer Command Line Tools release is available.
Update them from Software Update in System Preferences or run:
  softwareupdate --all --install --force

If that doesn't show you any updates, run:
  sudo rm -rf /Library/Developer/CommandLineTools
  sudo xcode-select --install

Alternatively, manually download them from:
  https://developer.apple.com/download/all/.
You should download the Command Line Tools for Xcode 13.1.

Error: Empty installation

I tried updating my command line tools. Now the CLI is gone from my system.

EventSub trigger: 410 is not red in panic

Consider this:

barrycarlyon@Robyn ~ % twitch event trigger stream-change -F http://127.0.0.1:8000/webhook/ -s gkldklgfhjksdfhjk --to-user 15185913
2021/09/19 17:09:42 [410] Request Sent
{"subscription":{"id":"57d6e7d8-4687-4799-0694-dbc10c3f7ff4","status":"enabled","type":"channel.update","version":"1","condition":{"broadcaster_user_id":"15185913"},"transport":{"method":"webhook","callback":"null"},"created_at":"2021-09-19T16:09:42.256805Z","cost":0},"event":{"broadcaster_user_id":"15185913","broadcaster_user_login":"testBroadcaster","broadcaster_user_name":"testBroadcaster","title":"Example title from the CLI!","language":"en","category_id":"9592","category_name":"Just Chatting","is_mature":false}}

barrycarlyon@Robyn ~ % twitch event trigger stream-change -F http://127.0.0.1:8000/webhook/ -s gkldklgfhjksdfhjk --to-user 15185913
2021/09/19 17:10:46 [200] Request Sent
{"subscription":{"id":"4844d8f1-5264-28c2-05a9-2945a599dfb0","status":"enabled","type":"channel.update","version":"1","condition":{"broadcaster_user_id":"15185913"},"transport":{"method":"webhook","callback":"null"},"created_at":"2021-09-19T16:10:46.113298Z","cost":0},"event":{"broadcaster_user_id":"15185913","broadcaster_user_login":"testBroadcaster","broadcaster_user_name":"testBroadcaster","title":"Example title from the CLI!","language":"en","category_id":"6888","category_name":"Just Chatting","is_mature":false}}

image

Since it responded with a 410 in my first test, surely that should be red in panic. As I didn't 200 Ok.

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.