twitter-dart / twitter-api-v2

The most famous and powerful Dart/Flutter library for Twitter API v2.0 🐦
https://pub.dev/packages/twitter_api_v2
BSD 3-Clause "New" or "Revised" License
167 stars 45 forks source link

Can't Upload An Image To Twitter #650

Closed forecaster-cyber closed 1 year ago

forecaster-cyber commented 1 year ago

This code needs to upload an image to twitter, get the media_id and post a tweet with that image and the text "#cats"

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;

Future <void> uploadToTwitter(String filePath) async{

  final twitter = v2.TwitterApi(
    bearerToken: 'My Bearer Key,

    oauthTokens: v2.OAuthTokens(
      consumerKey: 'myconsumerkey',
      consumerSecret: 'myconsumersecret',
      accessToken: 'myaccestoken',
      accessTokenSecret: 'myaccestokensecret,
    ),
  );

  try {
    final uploadedMedia = await twitter.media.uploadMedia(
      file: File(filePath),
      onProgress: (event) {
        switch (event.state) {
          case v2.UploadState.preparing:
            print('Upload is preparing...');
            break;
          case v2.UploadState.inProgress:
            print('${event.progress}% completed...');
            break;
          case v2.UploadState.completed:
            print('Upload has completed!');
            break;
        }
      },
    );

    print(uploadedMedia.data.id);
    await twitter.tweets.createTweet(
      text: '#cats',
      media: v2.TweetMediaParam(
        mediaIds: [uploadedMedia.data.id],
      ),
    );
  } on v2.TwitterUploadException catch (e) {
    print(e);
  }

}

Then, I import this file to another file that shows a dialog, takes a picture and is supposed to post the tweet:


import 'dart:async';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:cats_twitter/mainPage.dart';
import 'package:cats_twitter/twitterTest.dart';

// A screen that allows users to take a picture using a given camera.
class TakePictureScreen extends StatefulWidget {
  const TakePictureScreen({
    super.key,
    required this.camera,
  });

  final CameraDescription camera;

  @override
  TakePictureScreenState createState() => TakePictureScreenState();
}

class TakePictureScreenState extends State<TakePictureScreen> {
  late CameraController _controller;
  late Future<void> _initializeControllerFuture;

  @override
  void initState() {
    super.initState();
    // To display the current output from the Camera,
    // create a CameraController.
    _controller = CameraController(
        //   // Get a specific camera from the list of available cameras.
        widget.camera,
        //   // Define the resolution to use.
        ResolutionPreset.medium,
        imageFormatGroup: ImageFormatGroup.yuv420);

    // Next, initialize the controller. This returns a Future.
    _initializeControllerFuture = _controller.initialize();
  }

  @override
  void dispose() {
    // Dispose of the controller when the widget is disposed.
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(10),
          color: Colors.white,
        ),
        width: screenWidth / 1.2,
        height: screenWidth * 1.2,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              width: screenWidth / 1.4,
              height: screenWidth / 1.2,
              child: FutureBuilder<void>(
                future: _initializeControllerFuture,
                builder: (context, snapshot) {
                  if (snapshot.connectionState == ConnectionState.done) {
                    // If the Future is complete, display the preview.
                    return CameraPreview(_controller);
                  } else {
                    // Otherwise, display a loading indicator.
                    return const Center(child: CircularProgressIndicator());
                  }
                },
              ),
            ),
            ElevatedButton(
              // Provide an onPressed callback.
              onPressed: () async {
                // Take the Picture in a try / catch block. If anything goes wrong,
                // catch the error.
                try {
                  // Ensure that the camera is initialized.
                  await _initializeControllerFuture;

                  // Attempt to take a picture and get the file `image`
                  // where it was saved.
                  final image = await _controller.takePicture();

                  if (!mounted) return;

                  Navigator.pop(context);

                  // If the picture was taken, display it on a new screen.

                  await showDialog(
                      context: context,
                      builder: ((context) {
                        return DisplayPictureScreen(imagePath: image.path);
                      }));
                } catch (e) {
                  // If an error occurs, log the error to the console.
                  print(e);
                }
              },
              child: const Icon(Icons.camera_alt),
            )
          ],
        ),
      ),
    );
  }
}

// A widget that displays the picture taken by the user.
class DisplayPictureScreen extends StatefulWidget {
  final String imagePath;

  const DisplayPictureScreen({super.key, required this.imagePath});

  @override
  State<DisplayPictureScreen> createState() => _DisplayPictureScreenState();
}

class _DisplayPictureScreenState extends State<DisplayPictureScreen> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(10),
          color: Colors.white,
        ),
        width: screenWidth / 1.2,
        height: screenWidth * 1.2,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Image.file(
              File(widget.imagePath),
              width: screenWidth,
              height: screenWidth / 1.2,
            ),
            ElevatedButton(
              // Provide an onPressed callback.
              onPressed: () async{
                await uploadToTwitter(widget.imagePath);
              },
              child: const Icon(Icons.ios_share_rounded),
            )
          ],
        ),
      ),
    );
  }
}

For some reason, when I run the app and take a picture, it returns a status code of 403 and:

0 tryJsonDecode (package:twitter_api_v2/src/core/util/json_utils.dart:21:5)

1 BaseService.checkResponse (package:twitter_api_v2/src/service/base_service.dart:487:5)

2 ServiceHelper.post (package:twitter_api_v2/src/core/service_helper.dart:156:39)

#3 BaseMediaService.post (package:twitter_api_v2/src/service/base_media_service.dart:68:7) #4 _MediaService._initUpload (package:twitter_api_v2/src/service/media/media_service.dart:511:7) #5 _MediaService._uploadMedia (package:twitter_api_v2/src/service/media/media_service.dart:452:26) #6 _MediaService.uploadMedia (package:twitter_api_v2/src/service/media/media_service.dart:310:7) #7 uploadToTwitter (package:cats_twitter/twitterTest.dart:22:27) #8 _DisplayPictureScreenState.build. (package:cats_twitter/openCamera.dart:148:17)
github-actions[bot] commented 1 year ago

Thanks for your contribution! :)

myConsciousness commented 1 year ago

Hi @forecaster-cyber ,

I tried and this endpoint works in my environment. There are several possibilities for a 403 Forbidden to be returned from Twitter's API server, is your access level Elevated Access?

The Media endpoint is based on Twitter API v1.1, so if you want to use the v1.1 endpoint, you must have Elevated Access permission.

github-actions[bot] commented 1 year ago

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] commented 1 year ago

This issue was closed because it has been inactive for 7 days since being marked as stale.