anchor.diff:    [MovieId] = [V || {"v", V} <- mochiweb_util:parse_qs(Query)],
     MovieId.
   
  +get_movie_title(#{<<"title">> := Title} = _Attachment) ->
  +  binary_to_list(Title).
  +
   handle_text_message(Text) ->
  -  case resolve_intent(Text) of
  -    {match, next, _Captured} ->
  -      agner_playlist:next();
  -    {match, volume, [Level]} ->
  -      agner_playlist:volume(Level);
  -    nomatch
  -      ->
  -      erlang:display(<<"nomatch">>),
  -      erlang:display(Text)
  -  end.
  +  Intent = resolve_intent(Text),
  +  handle_intent(Intent).
   
   resolve_intent(Text) ->
     resolve_intent(Text, [
  
positive.code: %%
  handle_frame({text, Body}) ->
    ParsedJson = jiffy:decode(Body, [return_maps]),
    case maps:get(<<"type">>, ParsedJson) of
      <<"message">> ->
        handle_message(ParsedJson);
      _Type ->
        unsupported_type
    end.
  
  handle_message(Message) ->
    case has_subtype(Message) of
      true ->
        case maps:get(<<"subtype">>, Message) of
          <<"message_changed">> ->
            handle_sub_message(maps:get(<<"message">>, Message));
          <<"channel_join">> ->
            erlang:display(<<"channel join">>),
            erlang:display(Message);
          _ ->
            erlang:display(<<"UNSUPPORTED SUBTYPE">>),
            erlang:display(Message)
        end;
      false ->
        handle_text_message(maps:get(<<"text">>, Message))
    end.
  
  has_subtype(Message) ->
    maps:is_key(<<"subtype">>, Message).
  
  handle_sub_message(SubMessage) ->
    case has_attachments(SubMessage) of
      true ->
        UserId = binary_to_list(maps:get(<<"user">>, SubMessage)),
        handle_attachments(UserId, maps:get(<<"attachments">>, SubMessage));
      false ->
        no_attachments
    end.
  
  has_attachments(Message) ->
    maps:is_key(<<"attachments">>, Message).
  
  handle_attachments(_UserId, []) ->
    ok;
  handle_attachments(UserId, [Head | Tail]) ->
    case maps:is_key(<<"video_html">>, Head) of
      true ->
        MovieId = extract_movie_id(maps:get(<<"from_url">>, Head)),
        Title = binary_to_list(maps:get(<<"title">>, Head)),
        agner_playlist:add(MovieId, Title, UserId);
      false ->
        no_attachments
    end,
  
    handle_attachments(UserId, Tail).
  
  extract_movie_id(MovieUrl) ->
    Decoded = cow_qs:urldecode(MovieUrl),
  
negative.code: resolve_intent(Text, [{Intent, Regex, Options} | Rest]) ->
    case re:run(Text, Regex, Options) of
      {match, Captured} ->
        {Intent, Captured};
      nomatch ->
        resolve_intent(Text, Rest)
    end;
  resolve_intent(Text, []) ->
    {nomatch, Text}.
  
  handle_intent({next, _Captured}) ->
    agner_playlist:next();
  handle_intent({volume, [Level]}) ->
    agner_playlist:volume(Level);
  handle_intent({nomatch, Text}) ->
    erlang:display(nomatch),
    erlang:display(Text).