Telegram Bot that retrieves book information from GoodReads
i = 0
for book in books:
    title = book[i].title
    info_dict['title'] = title

    author = book[i].authors[0]
    info_dict['author'] = author

    rating = book[i].average_rating
    info_dict['rating'] = rating

    review = book[i].description
    info_dict['review'] = review

    cover = book[i].image_url
    info_dict['cover'] = cover

    link = book[i].link
    info_dict['link'] = link
    i += 1
    book_list.append(info_dict)


I don't really get this part, either the books objects got some weird structure, or it seems like (and most likely is) that each object in this collection got also information about the whole collection.
So this might be just replaced with:

for book in books:
    book_list.append({
        'title': book.title,
        'author': book.authors[0],
        'rating': book.rating,
        'review': book.review,
        'cover': book.image_url,
        'link': book.link
    })


which you might want to replace with list comprehession if you want to:

book_list = [{'title': book.title,
              'author': book.authors[0],
              'rating': book.rating,
              'review': book.review,
              'cover': book.image_url,
              'link': book.link
              } for book in books]


But if I'm wrong and this books got really weird structure you can simplify your code as:

for i, book in enumerate(books):
    book_data = book[i]
    book_list.append({
        'title': book_data.title,
        'author': book_data.authors[0],
        'rating': book_data.rating,
        'review': book_data.review,
        'cover': book_data.image_url,
        'link': book_data.link
    })


now your random_book function can be prettified a bit as:

def random_book():
    index = random.randint(1, 1000000)
    book = gc.book(index)

    return {
        'title': book_data.title,
        'author': book_data.authors[0],
        'rating': book_data.rating,
        'review': book_data.review,
        'cover': book_data.image_url,
        'link': book_data.link
    }


Now as we can see, these both two functions are having deal with same structure, so you might want to create a parse function. And in the end your code will be like:

import random
from goodreads import client

client_key = "[redacted]"
client_secret = "[redacted]"

gc = client.GoodreadsClient(client_key, client_secret)

def parse_book(book):
    return {
        'title': book_data.title,
        'author': book_data.authors[0],
        'rating': book_data.rating,
        'review': book_data.review,
        'cover': book_data.image_url,
        'link': book_data.link
    }

def book_info(title):
    books = gc.search_books(str(title), page=1, search_field='all')

    return map(parse_book, books)

def random_book():
    index = random.randint(1, 1000000)
    book = gc.book(index)

    return parse_book(book)


Also in random_book I would replace index = random.randint(1, 1000000) with a call that returns a number of books in goodreads (if there is such) so you wont get an error here.