Coder Social home page Coder Social logo

draw's Introduction

DRAW: The Dart Reddit API Wrapper

Build Status Pub Version Coverage Status

DRAW, also known as the Dart Reddit API Wrapper, is a Dart package that provides simple access to the Reddit API. DRAW is inspired by PRAW, the Python Reddit API Wrapper, and aims to also maintain a similar interface.

Want to get involved? Check out how to contribute to get started!

Disclaimer: This is not an official Google product.

Installation

Installing DRAW is simple using Dart's package management system, pub. Instructions on how to import DRAW into your project can be found here. If you would prefer to live on the hemorrhaging-edge, methods to depend on a local copy of DRAW or on the Github repository can be found here.

Getting Started

Assuming you already have your Reddit OAuth credentials, getting started with DRAW is simple:

import 'dart:async';
import 'package:draw/draw.dart';

Future<void> main() async {
  // Create the `Reddit` instance and authenticated
  Reddit reddit = await Reddit.createScriptInstance(
    clientId: CLIENT_ID,
    clientSecret: SECRET,
    userAgent: AGENT_NAME,
    username: "DRAWApiOfficial",
    password: "hunter12", // Fake
  );

  // Retrieve information for the currently authenticated user
  Redditor currentUser = await reddit.user.me();
  // Outputs: My name is DRAWApiOfficial
  print("My name is ${currentUser.displayName}");
}

This simple example is a great way to confirm that DRAW is working and that your credentials have been configured correctly.

Web Authentication

To authenticate via the Reddit authentication page, the web authentication flow needs to be used. This requires that a web application is registered with a valid Reddit account, which provides a client-id and a client-secret. As part of this process, a redirect URL is associated with the registered web application. These three values are all that is needed to complete the web authentication flow.

Here is a simple example of how to use web authentication with DRAW:

import 'package:draw/draw.dart';

main() async {
  final userAgent = 'foobar';
  final configUri = Uri.parse('draw.ini');

  // Create a `Reddit` instance using a configuration file in the
  // current directory.
  final reddit = Reddit.createWebFlowInstance(userAgent: userAgent,
                                              configUri: configUri);

  // Build the URL used for authentication. See `WebAuthenticator`
  // documentation for parameters.
  final auth_url = reddit.auth.url(['*'], 'foobar');

  // ...
  // Complete authentication at `auth_url` in the browser and retrieve
  // the `code` query parameter from the redirect URL.
  // ...

  // Assuming the `code` query parameter is stored in a variable
  // `auth_code`, we pass it to the `authorize` method in the
  // `WebAuthenticator`.
  await reddit.auth.authorize(auth_code);

  // If everything worked correctly, we should be able to retrieve
  // information about the authenticated account.
  print(await reddit.user.me());
}

It is also possible to restore cached credentials in order to avoid the need to complete the web authentication flow on each run:

import 'package:draw/draw.dart';

// Provides methods to load and save credentials.
import 'credential_loader.dart';

main() async {
  final userAgent = 'foobar';
  final configUri = Uri.parse('draw.ini');

  // Load cached credentials from disk, if available.
  final credentialsJson = await loadCredentials();

  var reddit;

  if (credentialsJson == null) {
    reddit =
        await Reddit.createWebFlowInstance(userAgent: userAgent,
                                           configUri: configUri);

    // Build the URL used for authentication. See `WebAuthenticator`
    // documentation for parameters.
    final auth_url = reddit.auth.url(['*'], 'foobar');

    // ...
    // Complete authentication at `auth_url` in the browser and retrieve
    // the `code` query parameter from the redirect URL.
    // ...

    // Assuming the `code` query parameter is stored in a variable
    // `auth_code`, we pass it to the `authorize` method in the
    // `WebAuthenticator`.
    await reddit.auth.authorize(auth_code);

    // Write credentials to disk.
    await writeCredentials(reddit.auth.credentials.toJson());
  } else {
    // Create a new Reddit instance using previously cached credentials.
    reddit = Reddit.restoreAuthenticatedInstance(
        userAgent: userAgent,
        configUri: configUri,
        credentialsJson: credentialsJson);
  }

  // If everything worked correctly, we should be able to retrieve
  // information about the authenticated account.
  print(await reddit.user.me());
}

Installed Application Authentication

For usage in environments where it is impossible to keep a client secret secure, the installed application flow should be used. This requires that an installed application is registered with a valid Reddit account, which provides a client-id. As part of this process, a redirect URL is associated with the registered installed application. These two values are all that is needed to complete the installed application authentication flow.

The installed application authentication flow is almost identical to the web authentication flow described above, and it is also possible to save and restore credentials for installed applications in a similar fashion.

Read up more about how to use this authentication flow on here.

Here is a simple example of how to use the installed application authentication flow with DRAW:

import 'package:draw/draw.dart';

main() async {
  final userAgent = 'foobar';
  final configUri = Uri.parse('draw.ini');

  // Create a `Reddit` instance using a configuration file in the current
  // directory. Unlike the web authentication example, a client secret does
  // not need to be provided in the configuration file.
  final reddit = Reddit.createInstalledFlowInstance(userAgent: userAgent,
                                                    configUri: configUri);

  // Build the URL used for authentication. See `WebAuthenticator`
  // documentation for parameters.
  final auth_url = reddit.auth.url(['*'], 'foobar');

  // ...
  // Complete authentication at `auth_url` in the browser and retrieve
  // the `code` query parameter from the redirect URL.
  // ...

  // Assuming the `code` query parameter is stored in a variable
  // `auth_code`, we pass it to the `authorize` method in the
  // `WebAuthenticator`.
  await reddit.auth.authorize(auth_code);

  // If everything worked correctly, we should be able to retrieve
  // information about the authenticated account.
  print(await reddit.user.me());
}

DRAW Configuration Files (draw.ini)

Here's an example draw.ini suitable for web based authentication:

default=default
reddit_url='https://www.reddit.com'
oauth_url=https://oauth.reddit.com
redirect_uri=https://www.google.com
client_id=YOUR_CLIENT_ID_HERE
client_secret=YOUR_SECRET_HERE
userAgent=draw_testing_agent

Here the redirect URI is set to https://www.google.com, but you'll need to replace that with whatever redirect you have registered.

The format of draw.ini configuration files is very similar to that of praw.ini files used by PRAW, although there may be some minor differences due to the .ini parser used by DRAW.

Frequently Asked Questions (FAQ)

Q: "I'm having trouble authenticating. What's wrong?"

Assuming the build status of DRAW is passing, there's likely something wrong with your credentials or user-agent. Here's some things to check:

  • Ensure your client ID and client secret match those provided by Reddit, applicable for your use case.
  • Try a new value for userAgent. Reddit rejects requests with commonly used user-agents like "foobar", "testing", or "reddit", so try using a randomly generated user-agent to make sure this isn't the issue you're seeing.

License

DRAW is provided under a BSD 3-clause license. Copyright (c), 2017, the DRAW Project Authors and Google LLC.

draw's People

Contributors

bkonyi avatar ckartik avatar donovanrost avatar lilja avatar lucascodert avatar stefanlobbenmeier avatar supremedeity avatar thatseli avatar tjarvstrand avatar yusefouda avatar

Stargazers

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

Watchers

 avatar  avatar  avatar  avatar  avatar

draw's Issues

Submission needs an entry to access data['media']

Basically before that

int get numComments => data['num_comments'];
. Is there any reason some of the data fields (e.g. media) do not have getters? We should also think about which type it would have, since the content of that field can vary. Examples:

From https://v.redd.it/gyh95hiqc0b11 - media: {reddit_video: {fallback_url: https://v.redd.it/gyh95hiqc0b11/DASH_9_6_M?source=fallback, height: 1080, width: 608, scrubber_media_url: https://v.redd.it/gyh95hiqc0b11/DASH_600_K, dash_url: https://v.redd.it/gyh95hiqc0b11/DASHPlaylist.mpd, duration: 14, hls_url: https://v.redd.it/gyh95hiqc0b11/HLSPlaylist.m3u8, is_gif: true, transcoding_status: completed}}

From Our Unit Test -

"media": {
                  "type": "youtube.com",
                  "oembed": {
                    "provider_url": "https://www.youtube.com/",
                    "title": "DRAW: Using Dart to Moderate Reddit Comments (DartConf 2018)",
                    "type": "video",
                    "html": "&lt;iframe width=\"600\" height=\"338\" src=\"https://www.youtube.com/embed/VqNU_CYVaXg?feature=oembed&amp;enablejsapi=1&amp;enablejsapi=1\" frameborder=\"0\" allow=\"autoplay; encrypted-media\" allowfullscreen&gt;&lt;/iframe&gt;",
                    "thumbnail_width": 480,
                    "height": 338,
                    "width": 600,
                    "version": "1.0",
                    "author_name": "Google Developers",
                    "provider_name": "YouTube",
                    "thumbnail_url": "https://i.ytimg.com/vi/VqNU_CYVaXg/hqdefault.jpg",
                    "thumbnail_height": 360,
                    "author_url": "https://www.youtube.com/user/GoogleDevelopers"
                  }

Does restoreAuthenticatedInstance need to be an async action?

It does not appear to actually do anything async - the await it uses is for a function that doesn't actually do any async actions. Am I understanding this wrong? The reason I'm asking it is that it being async causes some difficulty when initializing my application, since I have to wait for it somehow before doing anything.

EDIT: Looking into this currently, will open a PR for it later, and maybe for some other things that might be useful to installed Flutter apps like mine.

How to get the user frontpage ?

Hi.
I am trying to get the user frontpage. With PRAW, after a little search on the Praw docs I've found out that the command is reddit.front.hot(), but Draw doesn't seem to have the front property for the Reddit object.
I have tried:

Redditor rdt = await reddit.user.me();
await for(UserContent submission in rdt.hot())
  {
    Submission s = await (submission as Submission).populate();

    print(s.body);
    print(s.selftext);
    print(s.url);
}

but this gives an error when calling rdt.hot() at line 76:

E/flutter ( 8691): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception:
E/flutter ( 8691): DRAWAuthenticationError: Status Code: 403 Reason: Forbidden
E/flutter ( 8691): #0      Authenticator._throwAuthenticationError (package:draw/src/auth.dart:240:5)
E/flutter ( 8691): #1      Authenticator._request (package:draw/src/auth.dart:194:7)
E/flutter ( 8691): <asynchronous suspension>
E/flutter ( 8691): #2      Authenticator.get (package:draw/src/auth.dart:125:12)
E/flutter ( 8691): <asynchronous suspension>
E/flutter ( 8691): #3      Reddit.get (package:draw/src/reddit.dart:301:33)
E/flutter ( 8691): <asynchronous suspension>
E/flutter ( 8691): #4      ListingGenerator.generator._nextBatch (package:draw/src/listing/listing_generator
.dart:49:38)
E/flutter ( 8691): <asynchronous suspension>
E/flutter ( 8691): #5      ListingGenerator.generator (package:draw/src/listing/listing_generator.dart:65:35
)
E/flutter ( 8691): <asynchronous suspension>
E/flutter ( 8691): #6      RedditBase&BaseListingMixin._buildGenerator (package:draw/src/listing/mixins/base
.dart:88:31)
E/flutter ( 8691): #7      RedditBase&BaseListingMixin.hot (package:draw/src/listing/mixins/base.dart:112:44
)
E/flutter ( 8691): #8      RedditApi.getUserFeed (/data/user/0/com.skuu.focusreddit/cache/focus_redditJDCDWH
/focus_reddit/lib/misc/reddit_api.dart:76:20)
E/flutter ( 8691): <asynchronous suspension>
E/flutter ( 8691): #9      _FeedPageState.initState (/data/user/0/com.skuu.focusreddit/cache/focus_redditJDC
DWH/focus_reddit/lib/pages/feed_page/feed_page.dart:40:15)
E/flutter ( 8691): #10     StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:3734:58)
E/flutter ( 8691): #11     ComponentElement.mount (package:flutter/src/widgets/framework.dart:3600:5)
E/flutter ( 8691): #12     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2890:14)
E/flutter ( 8691): #13     Element.updateChild (package:flutter/src/widgets/framework.dart:2693:12)
E/flutter ( 8691): #14     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3636:
16)
E/flutter ( 8691): #15     Element.rebuild (package:flutter/src/widgets/framework.dart:3478:5)
E/flutter ( 8691): #16     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3605:5)
E/flutter ( 8691): #17     ComponentElement.mount (package:flutter/src/widgets/framework.dart:3600:5)
E/flutter ( 8691): #18     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2890:14)
E/flutter ( 8691): #19     Element.updateChild (package:flutter/src/widgets/framework.dart:2693:12)
E/flutter ( 8691): #20     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3636:
16)
E/flutter ( 8691): #21     Element.rebuild (package:flutter/src/widgets/framework.dart:3478:5)
E/flutter ( 8691): #22     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3605:5)
E/flutter ( 8691): #23     ComponentElement.mount (package:flutter/src/widgets/framework.dart:3600:5)
E/flutter ( 8691): #24     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2890:14)
E/flutter ( 8691): #25     Element.updateChild (package:flutter/src/widgets/framework.dart:2693:12)
E/flutter ( 8691): #26     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:4633:14)
E/flutter ( 8691): #27     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2890:14)
E/flutter ( 8691): #28     Element.updateChild (package:flutter/src/widgets/framework.dart:2693:12)
E/flutter ( 8691): #29     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:4633:14)
E/flutter ( 8691): #30     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2890:14)
E/flutter ( 8691): #31     Element.updateChild (package:flutter/src/widgets/framework.dart:2693:12)
E/flutter ( 8691): #32     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:4633:14)
E/flutter ( 8691): #33     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2890:14)
E/flutter ( 8691): #34     Element.updateChild (package:flutter/src/widgets/framework.dart:2693:12)
E/flutter ( 8691): #35     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3636:16)
E/flutter ( 8691): #36     Element.rebuild (package:flutter/src/widgets/framework.dart:3478:5)
E/flutter ( 8691): #37     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3605:5)
E/flutter ( 8691): #38     StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:3752:22)
E/flutter ( 8691): #39     ComponentElement.mount (package:flutter/src/widgets/framework.dart:3600:5)
E/flutter ( 8691): #40     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2890:14)
E/flutter ( 8691): #41     Element.updateChild (package:flutter/src/widgets/framework.dart:2693:12)
E/flutter ( 8691): #42     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3636:16)
E/flutter ( 8691): #43     Element

Whereas reddit.subreddit('python').hot(), for example, works with no problems. I can confirm that I am logged in, and that I can get my name or karma.

Comments replaceMore gives an error

Hi!

I was trying to replace all the 'replace more' comments in a CommentForest, but somehow it didn't seem to work and threw this error:

E/flutter (28141): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: NoSuchMethodError: The getter '_comments' was called on null.
E/flutter (28141): Receiver: null
E/flutter (28141): Tried calling: _comments
E/flutter (28141): #0      Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:50:5)
E/flutter (28141): #1      CommentForest._insertComment (package:draw/src/models/comment_forest.dart:53:22)
E/flutter (28141): #2      List.forEach (dart:core/runtime/libarray.dart:106:8)
E/flutter (28141): #3      CommentForest.replaceMore (package:draw/src/models/comment_forest.dart:125:19)

This is the code I was using:

Submission post = await reddit.submission(id: id).populate();
await post.refresh();
await post.refreshComments();
await post.comments.replaceMore();

CompactLogin throws an error when auth.url()

auth.url(['edit','account','identity','report','privatemessages','read','save','submit','mysubreddits','vote','subscribe'], 'RaNdOm_StRiNg', duration: 'permanent', compactLogin: true).toString();

gives me an error:

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (24594): The following assertion was thrown building LoginPage(dirty, state: _LoginPageState#ff6ad):
I/flutter (24594): The path should end with "authorize?"
I/flutter (24594): 'package:draw/src/auth.dart': Failed assertion: line 377: 'path.endsWith("?")'
I/flutter (24594):
I/flutter (24594): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter (24594): more information in this error message to help you determine and fix the underlying cause.
I/flutter (24594): In either case, please report this assertion by filing a bug on GitHub:
I/flutter (24594):   https://github.com/flutter/flutter/issues/new
I/flutter (24594):
I/flutter (24594): When the exception was thrown, this was the stack:
I/flutter (24594): #2      WebAuthenticator.url (package:draw/src/auth.dart:377)
I/flutter (24594): #3      ApiReddit.getAuthUrl (file:///C:/Users/Adrian/Documents/MyApps/focus_reddit/lib/misc/api_reddit.dart:28)
I/flutter (24594): #4      _LoginPageState.loginWebView (file:///C:/Users/Adrian/Documents/MyApps/focus_reddit/lib/pages/login_page.dart:103)
I/flutter (24594): #5      _LoginPageState.build (file:///C:/Users/Adrian/Documents/MyApps/focus_reddit/lib/pages/login_page.dart:51)
I/flutter (24594): #6      StatefulElement.build (package:flutter/src/widgets/framework.dart:3713)
I/flutter (24594): #7      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3625)
I/flutter (24594): #8      Element.rebuild (package:flutter/src/widgets/framework.dart:3478)
I/flutter (24594): #9      BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2225)
I/flutter (24594): #10     BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&RendererBinding&WidgetsBinding.drawFrame
(package:flutter/src/widgets/binding.dart:621)
I/flutter (24594): #11     BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&RendererBinding._handlePersistentFrameCal
lback (package:flutter/src/rendering/binding.dart:208)
I/flutter (24594): #12     BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/bindi
ng.dart:990)
I/flutter (24594): #13     BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.da
rt:930)
I/flutter (24594): #14     BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.d
art:842)
I/flutter (24594): #15     _invoke (file:///b/build/slave/Linux_Engine/build/src/flutter/lib/ui/hooks.dart:120)
I/flutter (24594): #16     _drawFrame (file:///b/build/slave/Linux_Engine/build/src/flutter/lib/ui/hooks.dart:109)
I/flutter (24594): (elided 2 frames from class _AssertionError)
I/flutter (24594): ════════════════════════════════════════════════════════════════════════════════════════════════════

image

It works fine if I remove compactLogin: true

Improve code coverage

With overall code coverage low, more tests need to be written to better exercise error cases

Way to set onCredentialsRefreshed

Could you please put a way to set the onCredentialsRefreshed in the AuthorizationCodeGrant so a top level notification is sent that the refresh token has been refreshed. I need this because when i use the installed flow, once i authorize i store the credentials so i can use the restore flow. But since i never update the credentials stored, after the expiration of the refresh token, DRAW refreshes it, but i never update my credentials. So then when the application is closed and then opened after a while the restore method fails since i give it the old refresh token.

Also i noticed the friend and unfriend return error "$username is not a know Redditor". I can open a separate issue for this, if thats what your prefer.

Tnx for the awesome API wrapper BTW :)
Oliver

Example of InstalledApp

Hello,

Is there anything in the pipeline for installed application workflows/documentation specifically without a secret?

Thanks

Current Authorization flow is HELL

Sorry for the extremely unprofessional title, but I'm switching over from a self designed API and I'm just running into confusing error after error. Methods that should be required aren't marked as such, there are too many optional parameters that require you to understand the library to decide when to leave out.
There are too many technical terms that I feel should be abstracted for ease of use.
This is my second time attempting to integrate DRAW into this app. This is more of a feedback issue. If you have some time, please get with me so I can sort out this login flow. Thanks!

Current error AFTER authenticating the user and running reddit.auth.authorize(code);:

  The authorization URL has not yet been generated.

Pub version conflicts

Because ini 1.1.0 requires SDK version <2.0.0 and no versions of ini match >1.1.0 <2.0.0, ini ^1.1.0 is forbidden.

This is what my local machine complains about when I run it. On pub.dartlang.org, a similar error is displayed here.

Because draw depends on color >=0.2.0 which requires SDK version <2.0.0, version solving failed.

How can this be? I check both color and ini pubspec files and neither of them require a dart SDK less than 2.0.

Authenticating with an installed app, app type

Hi!

How could I init DRAW if I have an installed app type for reddit? Does DRAW support this type of app?
I've tried the base Reddit.createInstance and specifying only the clientId, redirectUri and userAgent, but it doesn't seem to work.

For instance, this is what I am trying to get:
Example

Thank you for the plugin, and for the help.

Some errors with variables types with Dart 2

I've updated Flutter to the new recent beta, and there seems to be some erorrs...

E/flutter ( 5496): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception:
E/flutter ( 5496): type 'Future<dynamic>' is not a subtype of type 'FutureOr<Redditor>' where
E/flutter ( 5496):   Future is from dart:async
E/flutter ( 5496):   FutureOr is from dart:async
E/flutter ( 5496):   Redditor is from package:draw/src/models/redditor.dart
E/flutter ( 5496):
E/flutter ( 5496): #0      User.me (package:draw/src/user.dart:51:19)
E/flutter ( 5496): <asynchronous suspension>
E/flutter ( 5496): #1      RedditApi.updateUserInfos (file:///C:/Users/Adrian/Documents/MyApps/focus_reddit/lib/misc/reddit_api.dart:68:28)

My code calls Redditor me = await reddit.user.me();

And here another one

E/flutter ( 5496): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception:
E/flutter ( 5496): type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>' where
E/flutter ( 5496):   _InternalLinkedHashMap is from dart:collection
E/flutter ( 5496):   Map is from dart:core
E/flutter ( 5496):   String is from dart:core
E/flutter ( 5496):
E/flutter ( 5496): #0      Authenticator._request (package:draw/src/auth.dart:166:53)
E/flutter ( 5496): <asynchronous suspension>
E/flutter ( 5496): #1      Authenticator.get (package:draw/src/auth.dart:125:12)
E/flutter ( 5496): <asynchronous suspension>
E/flutter ( 5496): #2      Reddit.get (package:draw/src/reddit.dart:334:33)
E/flutter ( 5496): <asynchronous suspension>
E/flutter ( 5496): #3      ListingGenerator.generator._nextBatch (package:draw/src/listing/listing_generator.dart:49:38)
E/flutter ( 5496): <asynchronous suspension>
E/flutter ( 5496): #4      ListingGenerator.generator (package:draw/src/listing/listing_generator.dart:65:35)
E/flutter ( 5496): <asynchronous suspension>
E/flutter ( 5496): #5      _FrontPage&RedditBase&BaseListingMixin._buildGenerator (package:draw/src/listing/mixins/base.dart:91:29)
E/flutter ( 5496): #6      _FrontPage&RedditBase&BaseListingMixin.hot (package:draw/src/listing/mixins/base.dart:112:44)
E/flutter ( 5496): #7      PostsLoader._loadUserFeed (file:///C:/Users/Adrian/Documents/MyApps/focus_reddit/lib/misc/posts_loader.dart:34:66)
E/flutter ( 5496): <asynchronous suspension>
E/flutter ( 5496): #8      new PostsLoader (file:///C:/Users/Adrian/Documents/MyApps/focus_reddit/lib/misc/posts_loader.dart:21:5)
E/flutter ( 5496): #9      FeedPageState.initState (file:///C:/Users/Adrian/Documents/MyApps/focus_reddit/lib/pages/feed_page/feed_page.dart:76:28)
E/flutter ( 5496): #10     StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:3743:58)
E/flutter ( 5496): #11     ComponentElement.mount (package:flutter/src/widgets/framework.dart:3609:5)
E/flutter ( 5496): #12     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2899:14)
E/flutter ( 5496): #13     Element.updateChild (package:flutter/src/widgets/framework.dart:2702:12)
E/flutter ( 5496): #14     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:4646:14)
E/flutter ( 5496): #15     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2899:14)
E/flutter ( 5496): #16     Element.updateChild (package:flutter/src/widgets/framework.dart:2702:12)
E/flutter ( 5496): #17     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
E/flutter ( 5496): #18     Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
E/flutter ( 5496): #19     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3614:5)
E/flutter ( 5496): #20     ComponentElement.mount (package:flutter/src/widgets/framework.dart:3609:5)
E/flutter ( 5496): #21     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2899:14)
E/flutter ( 5496): #22     Element.updateChild (package:flutter/src/widgets/framework.dart:2702:12)
E/flutter ( 5496): #23     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:4646:14)
E/flutter ( 5496): #24     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2899:14)
E/flutter ( 5496): #25     Element.updateChild (package:flutter/src/widgets/framework.dart:2702:12)
E/flutter ( 5496): #26     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:4646:14)
E/flutter ( 5496): #27     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2899:14)
E/flutter ( 5496): #28     Element.updateChild (package:flutter/src/widgets/framework.dart:2702:12)
E/flutter ( 5496): #29     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:4646:14)
E/flutter ( 5496): #30     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2899:14)
E/flutter ( 5496): #31     Element.updateChild (package:flutter/src/widgets/
etc

My code is calling
RedditApi.reddit.front.hot();

submission.refreshComments can not be sorted

Example:

Submission submission = await reddit.front.top().first;
    CommentForest commentForest = await submission.refreshComments(sort: CommentSortType.newest);
    Comment comment = commentForest.comments.first as Comment;
    print("Submission ${submission.title} ${comment.createdUtc} ${comment.author} ${comment.permalink}");

will yield:

Submission My cab driver tonight was so excited to share with me that he’d made the cover of the calendar. I told him I’d help let the world see 2017-12-28 03:15:53.000Z anniemiss /r/funny/comments/7mjw12/my_cab_driver_tonight_was_so_excited_to_share/druihai/

Now check https://reddit.com/r/funny/comments/7mjw12/my_cab_driver_tonight_was_so_excited_to_share/druihai/

You will see that its actually the top comment. I checked the source code and I could not find any lines actually using the CommentSort to change the request.

"OS not Recognized by Draw" thrown when attempting to run on Android

In the config_file_reader._getUserConfigPath() method, a check occurs for macOS, Linus, and Windows. When run on Android (and presumably iOS), all checks will fail and it will fall through to throwing an error.
This makes usage with Flutter a non-option.

Additional checks should be added for Platform.isAndroid and Platform.isIOS

Calling refreshComments throws a failed assertion error

I have this method:

Future initComments() async {
CommentForest comments = await submission.refreshComments();
return comments;
}

For some reason when I use the CommentForest returned by it I'm always receiving this error:

'package:draw/src/models/submission_impl.dart': Failed assertion: line 38 pos 10: '!s._commentsById.containsKey(c.fullname)': is not true.

I remove the assert on line 38 and the problem is gone. Anyone have any ideas why this might be happening?

Ability to login as Installed Application OAuth2?

Hi, I've taken a look at the lib and it looks like you can only use it as WebApp or ScriptApp.

I wish to use it in Flutter: is there a plan to add a new method of login that uses UUID and not a secret? or is this lib pure dart only?

Thanks in advance for the answer.

Redirect URI parses the string

my sample redirect_uri is http://localhost:8080

but when I print the auth_url, it prints http%3A%2F%2Flocalhost%3A8080%2F

And then when I paste the url to postman, (and remove the .compact attribute) I get

you sent an invalid request

— invalid redirect_uri parameter.

Then still on postman, when I change it to the unparsed string, i get the correct reponse (the page with Allow / Deny buttons

How to retrieve posts in a fashion that isn't so slow

Hello, I am attempting to use this API to create a Reddit flutter app. I am attempting to retrieve posts using the stream.submissions(); function and doing an asynchronous for loop as such:

stream = currentUser.reddit.subreddit('android').stream.submissions(); await for (UserContent submission in stream) { Submission s = submission; _postsTemp.add(s); setState(() { _posts = _postsTemp; }); debugPrint('new posts'); } debugPrint("My name is ${currentUser.displayName}");

However, when I do this it seems that the API simply returns 100 posts all at once, which is quite slow and takes a varying number of seconds to load. Is there a way that is faster? Can I run an event as new data comes in the stream? For example, once one post has been retrieved, is it possible for me to run an event at this time while waiting for more posts to load?

Some documentation on this topic would be helpful for newbies like me :) thank you for all your work!

Can't get a submission comments

This is my code:

void test() async
{
  int timer = 4;

  var subreddit = reddit.subreddit('python').hot();
  await for(UserContent submission in subreddit)
  {
    if(timer > 0)
    {
      Submission s = submission;

      if(!s.stickied)
      {
        print('Submission id: ' + s.id);
        print('Title: ' + s.title);
        print('Num upvotes: ' + s.upvotes.toString());
        print('Num comments: ' + s.numComments.toString());

        List<Comment> comments = s.comments.comments;
        for (var comment in comments)
        {
          print(comment.body);
        }

        timer--;
      }
    }
    else break;
  }
}

And this is the error:

[ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception:
E/flutter ( 4659): NoSuchMethodError: The getter 'comments' was called on null.
E/flutter ( 4659): Receiver: null
E/flutter ( 4659): Tried calling: comments
E/flutter ( 4659): #0      Object.noSuchMethod (dart:core-patch/dart:core/object_patch.dart:46)
E/flutter ( 4659): #1      ApiReddit.test (/data/user/0/com.skuu.focusreddit/cache/focus_redditQGUIDF/focus_reddit/lib/misc/api_reddit.dart:80:47)
E/flutter ( 4659): <asynchronous suspension>
E/flutter ( 4659): #2      ApiReddit.authorizeUser (/data/user/0/com.skuu.focusreddit/cache/focus_redditQGUIDF/focus_reddit/lib/misc/api_reddit.dart:36:7)
E/flutter ( 4659): <asynchronous suspension>
E/flutter ( 4659): #3      _LoginPageState.initState.<anonymous closure> (/data/user/0/com.skuu.focusreddit/cache/focus_redditQGUIDF/focus_reddit/lib/pages/login_page.dart:4
1:19)
E/flutter ( 4659): #4      _RootZone.runUnaryGuarded (dart:async/zone.dart:1316)
E/flutter ( 4659): #5      _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:330)
E/flutter ( 4659): #6      _DelayedData.perform (dart:async/stream_impl.dart:578)
E/flutter ( 4659): #7      _StreamImplEvents.handleNext (dart:async/stream_impl.dart:694)
E/flutter ( 4659): #8      _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:654)
E/flutter ( 4659): #9      _microtaskLoop (dart:async/schedule_microtask.dart:41)
E/flutter ( 4659): #10     _startMicrotaskLoop (dart:async/schedule_microtask.dart:50)

Where ApiReddit.test (/data/user/0/com.skuu.focusreddit/cache/focus_redditQGUIDF/focus_reddit/lib/misc/api_reddit.dart:80:47) is List<Comment> comments = s.comments.comments;.

Am I doing something wrong?

Search '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, String>'

When you try to search a subreddit... example:

await reddit.subreddit("all").search(text, sort: Sort.relevance, params: Map<String, String>()).toList()

The method search cannot be called because it throws:
_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, String>

I believe its this line thats the cause:
final data = (params != null) ? Map.from(params) : Map();
This alters the map and then when it gets passed to
ListingGenerator.createBasicGenerator
It throws becuase the map is
Map<dynamic ,dynamic>

I also tried passing null as params parameter but got the same result.

Oliver

Quiver 2.0 Incompatibility.

The current version of DRAW 0.4.1 is not compatible with quiver 2.0

Because every version of flutter_test from sdk depends on quiver 2.0.0+1 and draw >=0.3.0 depends on quiver >=0.26.2 <1.0.0, flutter_test from sdk is incompatible with draw >=0.3.0.

DRAWAuthenticationError: Status Code: unauthorized_client Reason: null

I'm running into DRAWAuthenticationError: Status Code: unauthorized_client Reason: null.

Full log:

E/flutter ( 2174): [ERROR:flutter/shell/common/shell.cc(184)] Dart Error: Unhandled exception:
E/flutter ( 2174): DRAWAuthenticationError: Status Code: unauthorized_client Reason: null
E/flutter ( 2174): #0      Reddit.createScriptInstance (package:draw/src/reddit.dart:202:25)
E/flutter ( 2174): <asynchronous suspension>
E/flutter ( 2174): #1      main (package:sreddit/main.dart:22:32)
E/flutter ( 2174): <asynchronous suspension>
E/flutter ( 2174): #2      _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:289:19)
E/flutter ( 2174): #3      _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)

I'm using this (or trying to), the way described:

Reddit reddit = await Reddit.createScriptInstance(
    clientId: "ID_HERE",
    clientSecret: "SECRET_HERE",
    userAgent: "SReddit",
    username: "MY_USERID",
    password: "MY_PASS",
  );

  Redditor currentUser = await reddit.user.me();
  print("Name: ${currentUser.displayName}");

What am I doing incorrectly here?

PubSpec details:
draw: ^0.4.6
dart sdk: ">=2.1.0-dev.9.4.flutter-f9ebf21297 <3.0.0"

Reddit class doesn't have comment attribute

To get a post by id I do:

Reddit reddit;
...
reddit.submission(id: postId).populate().then((s) => s.upvote());

But there doesn't seem to be a way to do this for a comment.

FormatException: Invalid OAuth response for "https://www.reddit.com/api/v1/access_token": required parameter "error" was not a string, was "401".

Hey, it's me again :) So far DRAW works perfectly, but I encountered another bug :( Initial authentication is working perfectly(Using createInstalledFlowInstance(...)). Also the restoring of an instance with restoreAuthenticatedInstance(...) is working… For about a few hours or so. Before that timefrme everything works. But after that timeframe it throws the following error:

E/flutter (30955): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: FormatException: Invalid OAuth response for "https://www.reddit.com/api/v1/access_token": required parameter "error" was not a string, was "401".
E/flutter (30955):
E/flutter (30955): {"message": "Unauthorized", "error": 401}
E/flutter (30955): #0 handleAccessTokenResponse
package:oauth2/src/handle_access_token_response.dart:95
E/flutter (30955): #1 Credentials.refresh
package:oauth2/src/credentials.dart:230
E/flutter (30955):
E/flutter (30955): #2 Client.refreshCredentials
package:oauth2/src/client.dart:155
E/flutter (30955):
E/flutter (30955): #3 WebAuthenticator.refresh
package:draw/src/auth.dart:518
E/flutter (30955):
E/flutter (30955): #4 Authenticator._request
package:draw/src/auth.dart:189
E/flutter (30955):
E/flutter (30955): #5 Authenticator.get
package:draw/src/auth.dart:143
E/flutter (30955):
E/flutter (30955): #6 Reddit.get
package:draw/src/reddit.dart:586
E/flutter (30955):
E/flutter (30955): #7 User.me

(I only copied the part of the stacktrace that is about DRAW)
There is no 2FA or something like that enabled on my account, that would remove the authentification from Reddits side(To my knowledge). Maybe I am doing something wrong?

The code for restoring the reddit instance:

RedditService.reddit = Reddit.restoreAuthenticatedInstance(
redditCredentials,
clientId: 'yG99FCj-------',
userAgent: SettingsService.getKey('redditUserAgent'),
redirectUri: Uri.parse('https://thatseliyt.de/')
);

i.redditmedia.com links need to unescape &amp;

For example, this link:

https://i.redditmedia.com/LO9bMLl0OFEAQ6XJPl6yXg1lv6Rdv_iHWIYw_Y8rgVg.png?fit=crop&amp;crop=faces%2Centropy&amp;arh=2&amp;w=1080&amp;s=dec028b663cc791538042d7df6e7c0b1

which was returned by a submission.preview, will return a 403 error. Remove all of the amp; and we get:

https://i.redditmedia.com/LO9bMLl0OFEAQ6XJPl6yXg1lv6Rdv_iHWIYw_Y8rgVg.png?fit=crop&crop=faces%2Centropy&arh=2&w=1080&s=dec028b663cc791538042d7df6e7c0b1

which works properly.

For now I'm calling .replaceAll("amp;", "") on my urls but I see no reason why this can't be done in the actual SubmissionPreview implementation

Getting an exception on createScriptInstance

I'm getting an error when calling createScriptInstance

Here's the exception:

[VERBOSE-2:ui_dart_state.cc(148)] Unhandled Exception: type 'Future<void>' is not a subtype of type 'Redditor' #0 main package:sirena/main.dart:35 #1 _runMainZoned.<anonymous closure>.<anonymous closure> (dart:ui/hooks.dart:170:23) #2 _rootRun (dart:async/zone.dart:1124:13) #3 _CustomZone.run (dart:async/zone.dart:1021:19) #4 _runZoned (dart:async/zone.dart:1516:10) #5 runZoned (dart:async/zone.dart:1500:12) #6 _runMainZoned.<anonymous closure> (dart:ui/hooks.dart:169:5) #7 _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:300:19) #8 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)

On breakpoint investigation, it continues to this line

// Retrieve information for the currently authenticated user Redditor currentUser = await reddit.user.me();

and successfully filling currentUser with the object and printing the display name with

print("My name is ${currentUser.displayName}");

This implies that the credentials are correct so let's tick that box already.

Thanks! <3

Unable to catch SocketException caused by lack of internet

Ive been at this for a few hours trying to figure it out and I'm stuck.

I've currently only tested this with Reddit.createUntrustedReadOnlyInstance so I'm not sure of other methods.

doing:

  try {
    var reddit = await Reddit.createUntrustedReadOnlyInstance(
        userAgent: userAgent, deviceId: "121212121212", clientId: clientId);
  } catch (e) {
    print(e);
  }

still spams the console
and even not using await and doing onError or .catchError still bubbles up to the console.

E/flutter (27985): [ERROR:flutter/shell/common/shell.cc(181)] Dart Error: Unhandled exception:
E/flutter (27985): SocketException: Failed host lookup: 'www.reddit.com' (OS Error: No address associated with hostname, errno = 7)
E/flutter (27985): #0      IOClient.send (package:http/src/io_client.dart:30:23)
E/flutter (27985): <asynchronous suspension>
E/flutter (27985): #1      BaseClient._sendUnstreamed (package:http/src/base_client.dart:171:38)
E/flutter (27985): <asynchronous suspension>
E/flutter (27985): #2      BaseClient.post (package:http/src/base_client.dart:56:5)
E/flutter (27985): #3      Authenticator._requestTokenUntrusted (package:draw/src/auth.dart:283:39)
E/flutter (27985): <asynchronous suspension>
E/flutter (27985): #4      ReadOnlyAuthenticator._authenticationFlow (package:draw/src/auth.dart:400:13)
E/flutter (27985): <asynchronous suspension>
E/flutter (27985): #5      ReadOnlyAuthenticator.createUntrusted (package:draw/src/auth.dart:386:25)
E/flutter (27985): <asynchronous suspension>
E/flutter (27985): #6      new Reddit._untrustedReadOnlyInstance (package:draw/src/reddit.dart:369:27)
E/flutter (27985): #7      Reddit.createUntrustedReadOnlyInstance (package:draw/src/reddit.dart:112:24)
E/flutter (27985): <asynchronous suspension>
E/flutter (27985): #8      main (file://<redacted>/lib/main.dart:32:31)
E/flutter (27985): <asynchronous suspension>
E/flutter (27985): #9      _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:289:19)
E/flutter (27985): #10     _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)

Error while authorizing: did not contain required parameter "access_token"

Hello,

firstly I am relatively new to Flutter so sorry if this is a problem on my side :)

I created a new project and imported the lib. Then I followed the guide on how to setup a "Installed Application". It works until i call reddit.auth.authorize(code);. Then it throws the error

FormatException (FormatException: Invalid OAuth response for "https://www.reddit.com/api/v1/access_token": did not contain required parameter "access_token".

and stops working. The Auth code seems to be correct, so I am a bit puzzled about this, maybe there was an update to the Reddit API endpoint and the lib isn't up-to-date anymore. 🤔 I uploaded the project(here), if you need to try it yourself. (If that implementation seems a bit janky, it is just a early prototype for leaning Flutter/Dart :) )

I hope you can help me to fix this issue and thanks in advance :)

Support draw.ini configurations on Fuchsia

Currently when we try to find a draw.ini configuration we bail out on Fuchsia and fall back to the arguments provided to Reddit.createInstance. This is low priority since there really isn't anyone targeting Fuchsia at this time.

More Comments quirk.

I noticed a weird quirk that probably has more to do with the reddit API than DRAW. But it might be relevant to DRAW

If the parent of a MoreComments is depth 9 -- which I believe is the max depth -- the count of that MoreComments will be 0, but it is still capable of having children. However the depth of those children will start over at 1 instead of the expected 10.

https://www.reddit.com/r/apolloapp/comments/bllmpu/apollo_for_reddit_app_and_subreddit_faq_do_you/.json

https://www.reddit.com/r/apolloapp/comments/bllmpu/apollo_for_reddit_app_and_subreddit_faq_do_you/emq3omu/.json

Here is an example of a post with a depth 9 comment and then the expanded 'more' with a depth 1 reply

Any way to get the newest posts without a stream?

If I want to implement pagination for the newest posts, I think directly accessing the /r/subreddit/new.json and providing convenience before() and after() method of some sort would be easier to work with than a stream. And getting the newest posts for 1 time only is also seemingly impossible without using stream.submissions(pauseAfter: 1).takeWhile((s) => s != null)).toList() or something similar to that, which results in a lot of loading time. Am I missing something in the API or is this the only way to get the newest submissions?

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.