{"action":"created","comment":{"url":"https://api.github.com/repos/stepnivlk/pushest/pulls/comments/179813423","pull_request_review_id":110131643,"id":179813423,"diff_hunk":"@@ -7,154 +9,176 @@ defmodule PushestTest do\n   alias Pushest.FakeClient\n \n   defmodule TestPushest do\n-    use Pushest\n-    @app_key \"APP_KEY\"\n-    @pusher_opts %{cluster: \"eu\", encrypted: true, secret: \"SECRET\"}\n+    @moduledoc false\n \n-    def start_link() do\n-      Pushest.start_link(@app_key, @pusher_opts, __MODULE__)\n-    end\n+    use Pushest, otp_app: :pushest\n   end\n \n-  describe \"subscribe\" do\n-    setup do\n-      FakeClient.start_link()\n+  def child_pid(mod_name) do\n+    Pushest.Supervisor\n+    |> Supervisor.which_children()\n+    |> Enum.find(fn {name, _, _, _} -> name == mod_name end)\n+    |> elem(1)\n+  end\n \n-      {:ok, pid} = TestPushest.start_link()\n+  @app_key \"PUSHER_APP_KEY\"\n \n-      FakeClient.setup(%{parent_pid: pid})\n+  @pusher_config %{\n+    app_id: \"PUSHER_APP_ID\",\n+    key: @app_key,\n+    secret: \"PUSHER_SECRET\",\n+    cluster: \"PUSHER_CLUSTER\",\n+    encrypted: true\n+  }\n \n-      [pid: pid]\n-    end\n+  def start() do\n+    {:ok, fake_client_pid} = FakeClient.start_link()\n+    {:ok, test_pushest_pid} = TestPushest.start_link(@pusher_config)\n+    Application.ensure_all_started(:pushest)\n+\n+    # :timer.sleep(500)\n+\n+    api_pid = child_pid(Pushest.Api)\n+    socket_pid = child_pid(Pushest.Socket)\n+\n+    FakeClient.establish_connection()\n \n+    [\n+      api_pid: api_pid,\n+      socket_pid: socket_pid,\n+      fake_client_pid: fake_client_pid,\n+      test_pushest_pid: test_pushest_pid\n+    ]\n+  end\n+\n+  setup_all do\n+    start()\n+  end\n+\n+  describe \"subscribe\" do\n     @channel \"test-channel\"\n     test \"to a public channel\", context do\n-      TestPushest.subscribe(context.pid, @channel)\n+      TestPushest.subscribe(@channel)\n \n-      :sys.get_state(context.pid)\n+      :sys.get_state(context.socket_pid)\n \n       {:ok, frame} = FakeClient.last_frame()\n \n       assert frame ==\n                ~s({\"event\":\"pusher:subscribe\",\"data\":{\"channel_data\":\"{}\",\"channel\":\"test-channel\",\"auth\":null},\"channel\":null})\n \n-      assert TestPushest.channels(context.pid) == [@channel]\n+      assert TestPushest.subscribed_channels() |> Enum.member?(@channel)\n     end\n \n     @channel \"private-channel\"\n     test \"to a private channel\", context do\n-      TestPushest.subscribe(context.pid, @channel)\n+      TestPushest.subscribe(@channel)\n \n-      :sys.get_state(context.pid)\n+      :sys.get_state(context.socket_pid)\n \n       {:ok, frame} = FakeClient.last_frame()\n \n       assert frame ==\n-               ~s({\"event\":\"pusher:subscribe\",\"data\":{\"channel_data\":\"{}\",\"channel\":\"private-channel\",\"auth\":\"APP_KEY:faf0cd4400fa3972e5a25ffc7f211bdc084c718182963ca918cdd1431e280ba3\"},\"channel\":null})\n+               ~s({\"event\":\"pusher:subscribe\",\"data\":{\"channel_data\":\"{}\",\"channel\":\"private-channel\",\"auth\":\"#{\n+                 @app_key\n+               }:489cbc51261a2aa3baaf69b2df8c521530e2c1d9443d4cc3716328189120b1e8\"},\"channel\":null})\n \n-      assert TestPushest.channels(context.pid) == [@channel]\n+      assert TestPushest.subscribed_channels() |> Enum.member?(@channel)","path":"test/pushest_test.exs","position":105,"original_position":105,"commit_id":"b66a4dd0e1c02d1b1d1bd781fdbee7e5385019bf","original_commit_id":"b66a4dd0e1c02d1b1d1bd781fdbee7e5385019bf","user":{"login":"stepnivlk","id":11438652,"avatar_url":"https://avatars0.githubusercontent.com/u/11438652?v=4","gravatar_id":"","url":"https://api.github.com/users/stepnivlk","html_url":"https://github.com/stepnivlk","followers_url":"https://api.github.com/users/stepnivlk/followers","following_url":"https://api.github.com/users/stepnivlk/following{/other_user}","gists_url":"https://api.github.com/users/stepnivlk/gists{/gist_id}","starred_url":"https://api.github.com/users/stepnivlk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stepnivlk/subscriptions","organizations_url":"https://api.github.com/users/stepnivlk/orgs","repos_url":"https://api.github.com/users/stepnivlk/repos","events_url":"https://api.github.com/users/stepnivlk/events{/privacy}","received_events_url":"https://api.github.com/users/stepnivlk/received_events","type":"User","site_admin":false},"body":"Use a function call when a pipeline is only one function long","created_at":"2018-04-06T16:43:02Z","updated_at":"2018-04-06T16:43:02Z","html_url":"https://github.com/stepnivlk/pushest/pull/7#discussion_r179813423","pull_request_url":"https://api.github.com/repos/stepnivlk/pushest/pulls/7","author_association":"OWNER","_links":{"self":{"href":"https://api.github.com/repos/stepnivlk/pushest/pulls/comments/179813423"},"html":{"href":"https://github.com/stepnivlk/pushest/pull/7#discussion_r179813423"},"pull_request":{"href":"https://api.github.com/repos/stepnivlk/pushest/pulls/7"}}},"pull_request":{"url":"https://api.github.com/repos/stepnivlk/pushest/pulls/7","id":179229633,"html_url":"https://github.com/stepnivlk/pushest/pull/7","diff_url":"https://github.com/stepnivlk/pushest/pull/7.diff","patch_url":"https://github.com/stepnivlk/pushest/pull/7.patch","issue_url":"https://api.github.com/repos/stepnivlk/pushest/issues/7","number":7,"state":"open","locked":false,"title":"Support both REST API and WS connections","user":{"login":"stepnivlk","id":11438652,"avatar_url":"https://avatars0.githubusercontent.com/u/11438652?v=4","gravatar_id":"","url":"https://api.github.com/users/stepnivlk","html_url":"https://github.com/stepnivlk","followers_url":"https://api.github.com/users/stepnivlk/followers","following_url":"https://api.github.com/users/stepnivlk/following{/other_user}","gists_url":"https://api.github.com/users/stepnivlk/gists{/gist_id}","starred_url":"https://api.github.com/users/stepnivlk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stepnivlk/subscriptions","organizations_url":"https://api.github.com/users/stepnivlk/orgs","repos_url":"https://api.github.com/users/stepnivlk/repos","events_url":"https://api.github.com/users/stepnivlk/events{/privacy}","received_events_url":"https://api.github.com/users/stepnivlk/received_events","type":"User","site_admin":false},"body":"**[WIP]**\r\n## What does this PR do?\r\nTypically pusher libraries are separated to a client and server-side ones with a distinct set of features.\r\n\r\nClient-side features include mainly subscription to channels and real-time event handling via WebSockets). A client can trigger only on `private-` or `presence-` channels. But to be able to subscribe to such a channel authorization token has to be provided with subscription frame.\r\nIn standard flow, auth token is being generated using server-side library usually exposed on an endpoint and consumed by a client-side library.\r\n\r\nServer-side libraries communicate with Pusher via classical REST endpoint. Those libs can trigger on any channel (without subscription) and can do so in batches. It provides auth token generation and can also query for list of all the channels.\r\n\r\nIn Elixir we can unify those two separate approaches into one package.\r\nWe can spawn one process handling WebSocket connection and another one handling API communication. According to a shape of a user-defined request, we then send it to one of those processes. E.g. when a user triggers on a channel, we check whether there's active subscription for that channel if so we trigger on Websockets process otherwise on the API process.\r\n![pushest](https://i.imgur.com/1ECyJs3.png)\r\n\r\nThis PR adds:\r\n* API features in form of a process maintaining a `gun` connection to the Pusher API endpoint.\r\n* Router to dynamically route requests to either API or Websockets process.\r\n* Supervisor for the API and Websocket processes.\r\n* New configuration style (trying to mimic ecto way of configuring):\r\n```elixir\r\n# Usage in target application\r\n# simple_client/config/config.exs\r\nconfig :simple_client, SimpleClient,\r\n  pusher_app_id: System.get_env(\"PUSHER_APP_ID\"),\r\n  pusher_key: System.get_env(\"PUSHER_APP_KEY\"),\r\n  pusher_secret: System.get_env(\"PUSHER_SECRET\"),\r\n  pusher_cluster: System.get_env(\"PUSHER_CLUSTER\"),\r\n  pusher_encrypted: true\r\n\r\n# ...\r\n\r\ndefmodule SimpleClient do\r\n  use Pushest, otp_app: :simple_client\r\n\r\n  def handle_event({:ok, \"public-channel\", \"first-event\"}, frame) do\r\n    # Process frame here\r\n  end\r\n  # ...\r\nend\r\n```\r\n* `channels/0` function which lists all the existing channels (via REST API https://pusher.com/docs/rest_api#method-get-channels).\r\nRenames original channels to `subscribed_channels/0` which lists only channels to which is our client subscribed (this list is maintained directly in the state of `Pushest.Socket` process)\r\n* full example app.\r\n\r\nNow, `use`d `Pushest` can be included in target app supervision tree and can be started during startup\r\n\r\nComments are more than welcome!\r\n\r\n---\r\n\r\n## TODO\r\n- [ ] async gun response processing in the `Api` module\r\n- [ ] tests\r\n- [ ] review state handling of each process and cross-module responsibilities\r\n- [ ] Possibility to enforce API trigger.\r\n- [ ] Maybe ditch gen_server in both `Api` and `Socket` modules in favor of something like a\r\n http://erlang.org/doc/man/proc_lib.html#start_link-3 and receive block.","created_at":"2018-04-03T21:49:56Z","updated_at":"2018-04-06T16:43:02Z","closed_at":null,"merged_at":null,"merge_commit_sha":"460dee4fa99ec242cc2f1345b828a92803d3ab1e","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":883548466,"url":"https://api.github.com/repos/stepnivlk/pushest/labels/WIP","name":"WIP","color":"5b9906","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/stepnivlk/pushest/pulls/7/commits","review_comments_url":"https://api.github.com/repos/stepnivlk/pushest/pulls/7/comments","review_comment_url":"https://api.github.com/repos/stepnivlk/pushest/pulls/comments{/number}","comments_url":"https://api.github.com/repos/stepnivlk/pushest/issues/7/comments","statuses_url":"https://api.github.com/repos/stepnivlk/pushest/statuses/b66a4dd0e1c02d1b1d1bd781fdbee7e5385019bf","head":{"label":"stepnivlk:1.5.0","ref":"1.5.0","sha":"b66a4dd0e1c02d1b1d1bd781fdbee7e5385019bf","user":{"login":"stepnivlk","id":11438652,"avatar_url":"https://avatars0.githubusercontent.com/u/11438652?v=4","gravatar_id":"","url":"https://api.github.com/users/stepnivlk","html_url":"https://github.com/stepnivlk","followers_url":"https://api.github.com/users/stepnivlk/followers","following_url":"https://api.github.com/users/stepnivlk/following{/other_user}","gists_url":"https://api.github.com/users/stepnivlk/gists{/gist_id}","starred_url":"https://api.github.com/users/stepnivlk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stepnivlk/subscriptions","organizations_url":"https://api.github.com/users/stepnivlk/orgs","repos_url":"https://api.github.com/users/stepnivlk/repos","events_url":"https://api.github.com/users/stepnivlk/events{/privacy}","received_events_url":"https://api.github.com/users/stepnivlk/received_events","type":"User","site_admin":false},"repo":{"id":126724945,"name":"pushest","full_name":"stepnivlk/pushest","owner":{"login":"stepnivlk","id":11438652,"avatar_url":"https://avatars0.githubusercontent.com/u/11438652?v=4","gravatar_id":"","url":"https://api.github.com/users/stepnivlk","html_url":"https://github.com/stepnivlk","followers_url":"https://api.github.com/users/stepnivlk/followers","following_url":"https://api.github.com/users/stepnivlk/following{/other_user}","gists_url":"https://api.github.com/users/stepnivlk/gists{/gist_id}","starred_url":"https://api.github.com/users/stepnivlk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stepnivlk/subscriptions","organizations_url":"https://api.github.com/users/stepnivlk/orgs","repos_url":"https://api.github.com/users/stepnivlk/repos","events_url":"https://api.github.com/users/stepnivlk/events{/privacy}","received_events_url":"https://api.github.com/users/stepnivlk/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/stepnivlk/pushest","description":"Bidirectional Pusher client in Elixir","fork":false,"url":"https://api.github.com/repos/stepnivlk/pushest","forks_url":"https://api.github.com/repos/stepnivlk/pushest/forks","keys_url":"https://api.github.com/repos/stepnivlk/pushest/keys{/key_id}","collaborators_url":"https://api.github.com/repos/stepnivlk/pushest/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/stepnivlk/pushest/teams","hooks_url":"https://api.github.com/repos/stepnivlk/pushest/hooks","issue_events_url":"https://api.github.com/repos/stepnivlk/pushest/issues/events{/number}","events_url":"https://api.github.com/repos/stepnivlk/pushest/events","assignees_url":"https://api.github.com/repos/stepnivlk/pushest/assignees{/user}","branches_url":"https://api.github.com/repos/stepnivlk/pushest/branches{/branch}","tags_url":"https://api.github.com/repos/stepnivlk/pushest/tags","blobs_url":"https://api.github.com/repos/stepnivlk/pushest/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/stepnivlk/pushest/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/stepnivlk/pushest/git/refs{/sha}","trees_url":"https://api.github.com/repos/stepnivlk/pushest/git/trees{/sha}","statuses_url":"https://api.github.com/repos/stepnivlk/pushest/statuses/{sha}","languages_url":"https://api.github.com/repos/stepnivlk/pushest/languages","stargazers_url":"https://api.github.com/repos/stepnivlk/pushest/stargazers","contributors_url":"https://api.github.com/repos/stepnivlk/pushest/contributors","subscribers_url":"https://api.github.com/repos/stepnivlk/pushest/subscribers","subscription_url":"https://api.github.com/repos/stepnivlk/pushest/subscription","commits_url":"https://api.github.com/repos/stepnivlk/pushest/commits{/sha}","git_commits_url":"https://api.github.com/repos/stepnivlk/pushest/git/commits{/sha}","comments_url":"https://api.github.com/repos/stepnivlk/pushest/comments{/number}","issue_comment_url":"https://api.github.com/repos/stepnivlk/pushest/issues/comments{/number}","contents_url":"https://api.github.com/repos/stepnivlk/pushest/contents/{+path}","compare_url":"https://api.github.com/repos/stepnivlk/pushest/compare/{base}...{head}","merges_url":"https://api.github.com/repos/stepnivlk/pushest/merges","archive_url":"https://api.github.com/repos/stepnivlk/pushest/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/stepnivlk/pushest/downloads","issues_url":"https://api.github.com/repos/stepnivlk/pushest/issues{/number}","pulls_url":"https://api.github.com/repos/stepnivlk/pushest/pulls{/number}","milestones_url":"https://api.github.com/repos/stepnivlk/pushest/milestones{/number}","notifications_url":"https://api.github.com/repos/stepnivlk/pushest/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/stepnivlk/pushest/labels{/name}","releases_url":"https://api.github.com/repos/stepnivlk/pushest/releases{/id}","deployments_url":"https://api.github.com/repos/stepnivlk/pushest/deployments","created_at":"2018-03-25T18:00:54Z","updated_at":"2018-03-31T15:03:28Z","pushed_at":"2018-04-06T16:42:20Z","git_url":"git://github.com/stepnivlk/pushest.git","ssh_url":"git@github.com:stepnivlk/pushest.git","clone_url":"https://github.com/stepnivlk/pushest.git","svn_url":"https://github.com/stepnivlk/pushest","homepage":"","size":102,"stargazers_count":2,"watchers_count":2,"language":"Elixir","has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":1,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit"},"forks":0,"open_issues":1,"watchers":2,"default_branch":"master"}},"base":{"label":"stepnivlk:master","ref":"master","sha":"43bd5ddb412de5831c2ba922dc2f26b3f463a302","user":{"login":"stepnivlk","id":11438652,"avatar_url":"https://avatars0.githubusercontent.com/u/11438652?v=4","gravatar_id":"","url":"https://api.github.com/users/stepnivlk","html_url":"https://github.com/stepnivlk","followers_url":"https://api.github.com/users/stepnivlk/followers","following_url":"https://api.github.com/users/stepnivlk/following{/other_user}","gists_url":"https://api.github.com/users/stepnivlk/gists{/gist_id}","starred_url":"https://api.github.com/users/stepnivlk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stepnivlk/subscriptions","organizations_url":"https://api.github.com/users/stepnivlk/orgs","repos_url":"https://api.github.com/users/stepnivlk/repos","events_url":"https://api.github.com/users/stepnivlk/events{/privacy}","received_events_url":"https://api.github.com/users/stepnivlk/received_events","type":"User","site_admin":false},"repo":{"id":126724945,"name":"pushest","full_name":"stepnivlk/pushest","owner":{"login":"stepnivlk","id":11438652,"avatar_url":"https://avatars0.githubusercontent.com/u/11438652?v=4","gravatar_id":"","url":"https://api.github.com/users/stepnivlk","html_url":"https://github.com/stepnivlk","followers_url":"https://api.github.com/users/stepnivlk/followers","following_url":"https://api.github.com/users/stepnivlk/following{/other_user}","gists_url":"https://api.github.com/users/stepnivlk/gists{/gist_id}","starred_url":"https://api.github.com/users/stepnivlk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stepnivlk/subscriptions","organizations_url":"https://api.github.com/users/stepnivlk/orgs","repos_url":"https://api.github.com/users/stepnivlk/repos","events_url":"https://api.github.com/users/stepnivlk/events{/privacy}","received_events_url":"https://api.github.com/users/stepnivlk/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/stepnivlk/pushest","description":"Bidirectional Pusher client in Elixir","fork":false,"url":"https://api.github.com/repos/stepnivlk/pushest","forks_url":"https://api.github.com/repos/stepnivlk/pushest/forks","keys_url":"https://api.github.com/repos/stepnivlk/pushest/keys{/key_id}","collaborators_url":"https://api.github.com/repos/stepnivlk/pushest/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/stepnivlk/pushest/teams","hooks_url":"https://api.github.com/repos/stepnivlk/pushest/hooks","issue_events_url":"https://api.github.com/repos/stepnivlk/pushest/issues/events{/number}","events_url":"https://api.github.com/repos/stepnivlk/pushest/events","assignees_url":"https://api.github.com/repos/stepnivlk/pushest/assignees{/user}","branches_url":"https://api.github.com/repos/stepnivlk/pushest/branches{/branch}","tags_url":"https://api.github.com/repos/stepnivlk/pushest/tags","blobs_url":"https://api.github.com/repos/stepnivlk/pushest/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/stepnivlk/pushest/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/stepnivlk/pushest/git/refs{/sha}","trees_url":"https://api.github.com/repos/stepnivlk/pushest/git/trees{/sha}","statuses_url":"https://api.github.com/repos/stepnivlk/pushest/statuses/{sha}","languages_url":"https://api.github.com/repos/stepnivlk/pushest/languages","stargazers_url":"https://api.github.com/repos/stepnivlk/pushest/stargazers","contributors_url":"https://api.github.com/repos/stepnivlk/pushest/contributors","subscribers_url":"https://api.github.com/repos/stepnivlk/pushest/subscribers","subscription_url":"https://api.github.com/repos/stepnivlk/pushest/subscription","commits_url":"https://api.github.com/repos/stepnivlk/pushest/commits{/sha}","git_commits_url":"https://api.github.com/repos/stepnivlk/pushest/git/commits{/sha}","comments_url":"https://api.github.com/repos/stepnivlk/pushest/comments{/number}","issue_comment_url":"https://api.github.com/repos/stepnivlk/pushest/issues/comments{/number}","contents_url":"https://api.github.com/repos/stepnivlk/pushest/contents/{+path}","compare_url":"https://api.github.com/repos/stepnivlk/pushest/compare/{base}...{head}","merges_url":"https://api.github.com/repos/stepnivlk/pushest/merges","archive_url":"https://api.github.com/repos/stepnivlk/pushest/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/stepnivlk/pushest/downloads","issues_url":"https://api.github.com/repos/stepnivlk/pushest/issues{/number}","pulls_url":"https://api.github.com/repos/stepnivlk/pushest/pulls{/number}","milestones_url":"https://api.github.com/repos/stepnivlk/pushest/milestones{/number}","notifications_url":"https://api.github.com/repos/stepnivlk/pushest/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/stepnivlk/pushest/labels{/name}","releases_url":"https://api.github.com/repos/stepnivlk/pushest/releases{/id}","deployments_url":"https://api.github.com/repos/stepnivlk/pushest/deployments","created_at":"2018-03-25T18:00:54Z","updated_at":"2018-03-31T15:03:28Z","pushed_at":"2018-04-06T16:42:20Z","git_url":"git://github.com/stepnivlk/pushest.git","ssh_url":"git@github.com:stepnivlk/pushest.git","clone_url":"https://github.com/stepnivlk/pushest.git","svn_url":"https://github.com/stepnivlk/pushest","homepage":"","size":102,"stargazers_count":2,"watchers_count":2,"language":"Elixir","has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":1,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit"},"forks":0,"open_issues":1,"watchers":2,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/stepnivlk/pushest/pulls/7"},"html":{"href":"https://github.com/stepnivlk/pushest/pull/7"},"issue":{"href":"https://api.github.com/repos/stepnivlk/pushest/issues/7"},"comments":{"href":"https://api.github.com/repos/stepnivlk/pushest/issues/7/comments"},"review_comments":{"href":"https://api.github.com/repos/stepnivlk/pushest/pulls/7/comments"},"review_comment":{"href":"https://api.github.com/repos/stepnivlk/pushest/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/stepnivlk/pushest/pulls/7/commits"},"statuses":{"href":"https://api.github.com/repos/stepnivlk/pushest/statuses/b66a4dd0e1c02d1b1d1bd781fdbee7e5385019bf"}},"author_association":"OWNER"}}
{"actor":{"display_login":"stepnivlk"}}